module ysyx_22050293_Decoder #(
    parameter IN_WIDTH  = 3,
    parameter OUT_WIDTH = 8
) (
    input   [IN_WIDTH-1:0]    in,
    output  [OUT_WIDTH-1:0]   out
);

    generate
        for (genvar i = 0; i < OUT_WIDTH; i = i + 1) begin: generate_decoder
            assign out[i] = (in == i);
        end
    endgenerate
    // assign out = 1 << in;

endmodule


module ysyx_22050293_Encoder_16_4 (
    input  [15:0]   in,
    output [3:0]    out
);
    
    assign out  = ({4{in[ 0]}} & 4'b0000)
                | ({4{in[ 1]}} & 4'b0001)
                | ({4{in[ 2]}} & 4'b0010)
                | ({4{in[ 3]}} & 4'b0011)
                | ({4{in[ 4]}} & 4'b0100)
                | ({4{in[ 5]}} & 4'b0101)
                | ({4{in[ 6]}} & 4'b0110)
                | ({4{in[ 7]}} & 4'b0111)
                | ({4{in[ 8]}} & 4'b1000)
                | ({4{in[ 9]}} & 4'b1001)
                | ({4{in[10]}} & 4'b1010)
                | ({4{in[11]}} & 4'b1011)
                | ({4{in[12]}} & 4'b1100)
                | ({4{in[13]}} & 4'b1101)
                | ({4{in[14]}} & 4'b1110)
                | ({4{in[15]}} & 4'b1111);

endmodule


module ysyx_22050293_Radix4BoothProduct #(
    parameter WIDTH = 68
) (
    input  [2:0]         y,
    input  [WIDTH-1:0]   x,
    output [WIDTH-1:0]   p,
    output               c
);

    wire sub_x  = ( y[2] &  y[1] & ~y[0]) | ( y[2] & ~y[1] & y[0]);
    wire add_x  = (~y[2] &  y[1] & ~y[0]) | (~y[2] & ~y[1] & y[0]);
    wire sub_2x = ( y[2] & ~y[1] & ~y[0]);
    wire add_2x = (~y[2] &  y[1] &  y[0]);

    // X[-1] = 0
    assign p[0] = (sub_x & ~x[0]) | (add_x & x[0]) | sub_2x;

    generate
        genvar i;
        for (i = 1; i < WIDTH; i = i + 1) begin : gen
            assign p[i] = (sub_x & ~x[i]) | (add_x & x[i])
                        | (sub_2x & ~x[i-1]) | (add_2x & x[i-1]);
        end
    endgenerate

    assign c = sub_x | sub_2x;

endmodule


module ysyx_22050293_ShiftDivider (
    input           clock,
    input           reset,
    input  [63:0]   x,
    input  [63:0]   y,
    input           sign,
    input           word,
    input           flush,
    input           valid,
    output          ready,
    output [63:0]   q,
    output [63:0]   r,
    output          done
);

    reg [127:0] X;
    reg [ 63:0] Y;
    reg [ 63:0] Q;
    reg [ 63:0] R;

    reg sign_R, word_R;
    reg msb_x_R, msb_y_R;

    wire [ 63:0] neg_x = ~x + 1;
    wire [ 63:0] neg_y = ~y + 1;

    wire [127:0] X_init = ({128{ word}} & {96'h0, (sign & x[31]) ? neg_x[31:0] : x[31:0]})  // 32 bit
                        | ({128{~word}} & {64'h0, (sign & x[63]) ? neg_x[63:0] : x[63:0]}); // 64 bit
    wire [ 63:0] Y_init = ({ 64{ word}} & {32'h0, (sign & y[31]) ? neg_y[31:0] : y[31:0]})  // 32 bit
                        | ({ 64{~word}} & {       (sign & y[63]) ? neg_y[63:0] : y[63:0]}); // 64 bit


    wire [64:0] delta_op1 = ({65{ word_R}} & {32'h0, X[ 63 -: 33]})  // 32 bit
                          | ({65{~word_R}} & {       X[127 -: 65]}); // 64 bit
    wire [64:0] delta_op2 = ({65{ word_R}} & {33'h0, Y[31:0]})  // 32 bit
                          | ({65{~word_R}} & { 1'b0, Y[63:0]}); // 64 bit

    wire [64:0] delta = delta_op1 - delta_op2;
    wire delta_sign = word_R ? delta[32] : delta[64];


    wire [ 63:0] Q_next_0   = ({64{ word_R}} & {32'h0, Q[30:0], 1'b0})      // 32 bit
                            | ({64{~word_R}} & {       Q[62:0], 1'b0});     // 64 bit
    wire [ 63:0] R_next_0   = ({64{ word_R}} & {32'h0, X[ 62 -: 32]})       // 32 bit
                            | ({64{~word_R}} & {       X[126 -: 64]});      // 64 bit
    wire [127:0] X_next_0   = ({128{ word_R}} & {64'h0, X[ 62:0], 1'b0})    // 32 bit
                            | ({128{~word_R}} & {       X[126:0], 1'b0});   // 64 bit

    wire [ 63:0] Q_next_1   = ({64{ word_R}} & {32'h0, Q[30:0], 1'b1})      // 32 bit
                            | ({64{~word_R}} & {       Q[62:0], 1'b1});     // 64 bit
    wire [ 63:0] R_next_1   = ({64{ word_R}} & {32'h0, delta[31:0]})        // 32 bit
                            | ({64{~word_R}} & {       delta[63:0]});       // 64 bit
    wire [127:0] X_next_1   = ({128{ word_R}} & {64'h0, delta[31:0], X[30:0], 1'b0})  // 32 bit
                            | ({128{~word_R}} & {       delta[63:0], X[62:0], 1'b0}); // 64 bit


    wire [63:0] neg_Q = ~Q + 1;
    wire [63:0] neg_R = ~R + 1;

    wire [63:0] q64 = (sign_R & (msb_x_R ^ msb_y_R)) ? neg_Q : Q;
    wire [63:0] r64 = (sign_R & msb_x_R) ? neg_R : R;

    assign q = word_R ? {32'h0, q64[31:0]} : q64[63:0];
    assign r = word_R ? {32'h0, r64[31:0]} : r64[63:0];


    localparam IDLE = 3'b001, CALC = 3'b010, DONE = 3'b100;

    reg [2:0] state;
    reg [7:0] count;

    assign ready = (state == IDLE);
    assign done  = (state == DONE);

    always @(posedge clock) begin
        if (reset | flush) begin
            state   <= IDLE;
            count   <= 0;
            { X, Y, Q, R }          <= 0;
            { sign_R, word_R }      <= 0;
            { msb_x_R, msb_y_R }    <= 0;
        end
        else begin
            case (state)
                IDLE: begin
                    // Start Calculation
                    if (valid & ready) begin
                        state <= CALC;
                        count <= word ? 32 : 64;
                        X <= X_init;
                        Y <= Y_init;
                        Q <= 0;
                        R <= 0;
                        sign_R <= sign;
                        word_R <= word;
                        msb_x_R <= word ? x[31] : x[63];
                        msb_y_R <= word ? y[31] : y[63];
                    end
                end
                CALC: begin
                    // Continue Calculation
                    if (|count) begin
                        state <= CALC;
                        count <= count - 1;
                        // quotient is 1
                        if (delta_sign) begin
                            Q <= Q_next_0;
                            R <= R_next_0;
                            X <= X_next_0;
                        end
                        // remainder is 1
                        else begin
                            Q <= Q_next_1;
                            R <= R_next_1;
                            X <= X_next_1;
                        end
                    end
                    // End Calculation
                    else begin
                        state   <= DONE;
                        count   <= 0;
                    end
                end
                // Calculation Completed
                DONE: begin
                    state <= IDLE;
                end
                default: begin
                    state   <= IDLE;
                end
            endcase
        end
    end

endmodule


module ysyx_22050293_ShiftMultiplier #(
    parameter WIDTH = 64,
    parameter E = WIDTH + 2,
    parameter W = E * 2
) (
    input                clock,
    input                reset,
    input  [WIDTH-1:0]   x,
    input  [WIDTH-1:0]   y,
    input  [1:0]         signs,
    input                valid,
    output               ready,
    output [2*WIDTH-1:0] result,
    output               done
);

    reg [E:0]       Y;
    reg [W-1:0]     X;
    reg [W-1:0]     R;
    reg             y_sign_R;

    assign result = R[2*WIDTH-1:0];

    // Partial Product
    wire [W-1:0]    p;
    wire            c;
    ysyx_22050293_Radix4BoothProduct #(
        .WIDTH(W)
    ) booth (
        .y      (Y[2:0]),
        .x      (X[W-1:0]),
        .p      (p),
        .c      (c)
    );

    // Adder
    wire [W-1:0]    adder_x     = R;
    wire [W-1:0]    adder_y     = p;
    wire            adder_cin   = c;
    wire [W-1:0]    adder_sum;
    assign adder_sum = adder_x + adder_y + {131'h0, adder_cin};

    // State Machine
    localparam IDLE = 3'b001, CALC = 3'b010, DONE = 3'b100;

    reg [2:0] state;
    assign ready = (state == IDLE);
    assign done  = (state == DONE);

    always @(posedge clock) begin
        if (reset) begin
            state       <= IDLE;
            y_sign_R    <= 0;
            X           <= 0;
            Y           <= 0;
            R           <= 0;
        end
        else begin
            case (state)
                IDLE: begin
                    // Start Calculation
                    if (valid & ready) begin
                        state       <= CALC;
                        y_sign_R    <= signs[0] & y[WIDTH-1];
                        X           <= { {(WIDTH+4){signs[1] & x[WIDTH-1]}}, x};
                        Y           <= { { 2{signs[0] & y[WIDTH-1]}}, y, 1'b0 };
                        R           <= 0;
                    end
                    else begin
                        state       <= IDLE;
                    end
                end
                CALC: begin
                    // Continue Calculation
                    if ((|Y[E-1:1]) | (~y_sign_R & Y[0])) begin
                        state   <= CALC;
                        X       <= { X[W-3:0], 2'b00 }; // X   <= X << 2;
                        Y       <= { 2'b00, Y[E:2] };   // Y   <= Y >> 2;
                        R       <= adder_sum;
                    end
                    // End Calculation
                    else begin
                        state   <= DONE;
                    end
                end
                // Calculation Completed
                DONE: begin
                    state       <= IDLE;
                end
                default: begin
                    state       <= IDLE;
                end
            endcase
        end
    end

endmodule //Multiplier


module ysyx_22050293_ALU (
    input           clock,
    input           reset,
    input           flush,
    input           valid,
    input   [11:0]  operation,
    input   [63:0]  src1,
    input   [63:0]  src2,
    input           word,
    input           unsign,
    input   [1:0]   mul_signs,
    input           mul_high,
    output  [63:0]  result,
    output          done
);

/* ------ 64-bit adder ------ */
    wire [63:0] adder_a;
    wire [63:0] adder_b;
    wire        adder_cin;
    wire [63:0] adder_sum;
    wire        adder_cout;
/* ------ 64-bit adder ------ */

/* ---------------------- operation flag ---------------------- */
    wire    add_op  = operation[00];	// 加法
    wire    sub_op  = operation[01];	// 减法
    wire    slt_op  = operation[02];	// 小于置位
    wire    and_op  = operation[03];	// 按位与
    wire    or_op   = operation[04];	// 按位或
    wire    xor_op  = operation[05];	// 按位异或
    wire    sll_op  = operation[06];	// 逻辑左移
    wire    srl_op  = operation[07];	// 逻辑右移
    wire    sra_op  = operation[08];	// 算术右移
    wire    mul_op  = operation[09];	// 乘法
    wire    div_op  = operation[10];	// 除法
    wire    rem_op  = operation[11];	// 取余
/* ---------------------- operation flag ---------------------- */


/* =============== ADD & SUB operation =============== */
    assign	adder_a   = src1;
    assign	adder_b   = (sub_op | slt_op) ? ~src2 : src2;
    assign	adder_cin = (sub_op | slt_op) ? 1'b1 : 1'b0;
    assign	{ adder_cout, adder_sum } = adder_a + adder_b + {64'h0, adder_cin};

    wire [63:0] add_sub_result		= adder_sum;
    wire [31:0] addw_subw_result	= adder_sum[31:0];
/* =============== ADD & SUB operation =============== */


/* ================== SLT operation ================== */
    wire    slt_s	= ( src1[63]  & ~src2[63])
                    | ((src1[63] ~^  src2[63]) & adder_sum[63]);
    wire    slt_u	= ~adder_cout;

    wire [63:0] slt_result = { 63'h0, unsign ? slt_u : slt_s };
    wire [31:0] sltw_result = slt_result[31:0];
/* ================== SLT operation ================== */


/* ================ Bitwise operation ================ */
    wire [63:0] and_result	= src1 & src2;
    wire [31:0] andw_result	= and_result[31:0];

    wire [63:0] or_result	= src1 | src2;
    wire [31:0] orw_result	= or_result[31:0];

    wire [63:0] xor_result	= src1 ^ src2;
    wire [31:0] xorw_result	= xor_result[31:0];
/* ================ Bitwise operation ================ */


/* ========================== Shift operation ========================== */
    wire [5:0] shamt = { word ? 1'b0 : src2[5], src2[4:0] };

    // Left-shift operation
    wire [63:0] sll_result = src1 << shamt;
    wire [31:0] sllw_result = sll_result[31:0];

    // Right-shift operation
    wire [127:0] sr_operand = word ? { {96{sra_op & src1[31]}}, src1[31:0] } : { {64{sra_op & src1[63]}}, src1[63:0] };
    /* lint_off UNUSEDSIGNAL */
    wire [127:0] sr128_result = sr_operand >> shamt;
    wire [63:0] sr_result = sr128_result[63:0];
    wire [31:0] srw_result = sr128_result[31:0];
/* ========================== Shift operation ========================== */


/* ========================== MUL operation ========================== */
    wire            mul_ready;
    wire            mul_done;
    wire [127:0]    mul128_result;
    reg             mul_doing;

    wire mul_valid  = valid & mul_op & ~mul_doing & ~mul_done;

    ysyx_22050293_ShiftMultiplier #(
        .WIDTH(64)
    ) multiplier (
        .clock  (clock),
        .reset  (reset),
        .x      (src1),
        .y      (src2),
        .signs  (mul_signs),
        .valid  (mul_valid),
        .ready  (mul_ready),
        .result (mul128_result),
        .done   (mul_done)
    );

    always @(posedge clock) begin
        if (reset) begin
            mul_doing <= 1'b0;
        end
        else if (mul_done) begin
            mul_doing <= 1'b0;
        end
        else if (mul_valid & mul_ready) begin
            mul_doing <= 1'b1;
        end
    end

    wire [63:0] mul_result = mul_high ? mul128_result[127:64] : mul128_result[63:0];
    wire [31:0] mulw_result = mul128_result[31:0];
/* ========================== MUL operation ========================== */


/* ========================== DIV & REM operation ========================== */
    wire [63:0] dr_q;
    wire [63:0] dr_r;
    wire        dr_ready;
    wire        dr_done;
    reg         dr_doing;

    wire dr_op      = (div_op | rem_op);
    wire dr_valid   = valid & dr_op & ~dr_doing & ~dr_done;

    ysyx_22050293_ShiftDivider divider64 (
        .clock      (clock),
        .reset      (reset),
        .x          (src1),
        .y          (src2),
        .sign       (~unsign),
        .word       (word),
        .flush      (flush),
        .valid      (dr_valid),
        .ready      (dr_ready),
        .q          (dr_q),
        .r          (dr_r),
        .done       (dr_done)
    );

    always @(posedge clock) begin
        if (reset) begin
            dr_doing <= 1'b0;
        end
        else if (dr_done) begin
            dr_doing <= 1'b0;
        end
        else if (dr_valid & dr_ready) begin
            dr_doing <= 1'b1;
        end
    end

    wire [63:0] div_result  = dr_q;
    wire [31:0] divw_result = dr_q[31:0];
    wire [63:0] rem_result  = dr_r;
    wire [31:0] remw_result = dr_r[31:0];
/* ========================== DIV & REM operation ========================== */


/* ---------------------------------------- result ---------------------------------------- */
    wire [31:0] result_32 = ({32{add_op | sub_op}}	& addw_subw_result)	|
                            ({32{slt_op         }}	& sltw_result     )	|
                            ({32{and_op         }}	& andw_result     )	|
                            ({32{or_op          }}	& orw_result      )	|
                            ({32{xor_op         }}	& xorw_result     )	|
                            ({32{sll_op         }}	& sllw_result     )	|
                            ({32{sra_op | srl_op}}	& srw_result      ) |
                            ({32{mul_op         }}	& mulw_result     )	|
                            ({32{div_op         }}	& divw_result     )	|
                            ({32{rem_op         }}	& remw_result     )	;

    wire [63:0] result_64 = ({64{add_op | sub_op}} & add_sub_result) |
                            ({64{slt_op         }} & slt_result    ) |
                            ({64{and_op         }} & and_result    ) |
                            ({64{or_op          }} & or_result     ) |
                            ({64{xor_op         }} & xor_result    ) |
                            ({64{sll_op         }} & sll_result    ) |
                            ({64{sra_op | srl_op}} & sr_result     ) |
                            ({64{mul_op         }} & mul_result    ) |
                            ({64{div_op         }} & div_result    ) |
                            ({64{rem_op         }} & rem_result    ) ;
    assign result = word ? {{32{result_32[31]}}, result_32} : result_64;
/* ---------------------------------------- result ---------------------------------------- */


    assign done =  (mul_op & mul_done)
                |  (dr_op & dr_done)
                | ~(mul_op | dr_op);

endmodule


module ysyx_22050293_AXI4Bridge #(
    parameter ADDR_WIDTH = 32,
    parameter DATA_WIDTH = 64,
    parameter MAKS_WIDTH = DATA_WIDTH / 8,

    parameter LINE_WIDTH = 128,
    parameter STRB_WIDTH = LINE_WIDTH / 8
) (
    input                    clock,
    input                    reset,

/* ********* AXI Master Interface ********* */
    // aw
    output [3:0]             io_master_awid,
    output [ADDR_WIDTH-1:0]  io_master_awaddr,
    output [7:0]             io_master_awlen,
    output [2:0]             io_master_awsize,
    output [1:0]             io_master_awburst,
    output                   io_master_awvalid,
    input                    io_master_awready,
    // w
    output [DATA_WIDTH-1:0]  io_master_wdata,
    output [MAKS_WIDTH-1:0]  io_master_wstrb,
    output                   io_master_wlast,
    output                   io_master_wvalid,
    input                    io_master_wready,
    // b
    /* lint_off UNUSEDSIGNAL */
    input  [3:0]             io_master_bid,
    input  [1:0]             io_master_bresp,
    input                    io_master_bvalid,
    output                   io_master_bready,
    // ar
    output [3:0]             io_master_arid,
    output [ADDR_WIDTH-1:0]  io_master_araddr,
    output [7:0]             io_master_arlen,
    output [2:0]             io_master_arsize,
    output [1:0]             io_master_arburst,
    output                   io_master_arvalid,
    input                    io_master_arready,
    // r
    input  [3:0]             io_master_rid,
    input  [DATA_WIDTH-1:0]  io_master_rdata,
    input  [1:0]             io_master_rresp,
    input                    io_master_rlast,
    input                    io_master_rvalid,
    output                   io_master_rready,
/* ********* AXI Master Interface ********* */

/* *********** ICache Interface *********** */
    input                    inst_rd_req,
    input  [ 2:0]            inst_rd_type,
    input  [ADDR_WIDTH-1:0]  inst_rd_addr,
    output                   inst_rd_rdy,
    output                   inst_ret_valid,
    output                   inst_ret_last,
    output [LINE_WIDTH-1:0]  inst_ret_data,
    /* lint_off UNUSEDSIGNAL */
    input                    inst_wr_req,
    input  [ 2:0]            inst_wr_type,
    input  [ADDR_WIDTH-1:0]  inst_wr_addr,
    input  [STRB_WIDTH-1:0]  inst_wr_wstrb,
    input  [LINE_WIDTH-1:0]  inst_wr_data,
    output                   inst_wr_rdy,
    output                   inst_read_fault,
    output                   inst_write_fault, // 0
/* *********** ICache Interface *********** */

/* *********** DCache Interface *********** */
    input                    data_rd_req,
    input  [ 2:0]            data_rd_type,
    input  [ADDR_WIDTH-1:0]  data_rd_addr,
    output                   data_rd_rdy,
    output                   data_ret_valid,
    output                   data_ret_last,
    output [LINE_WIDTH-1:0]  data_ret_data,

    input                    data_wr_req,
    input  [ 2:0]            data_wr_type,
    input  [ADDR_WIDTH-1:0]  data_wr_addr,
    input  [STRB_WIDTH-1:0]  data_wr_wstrb,
    input  [LINE_WIDTH-1:0]  data_wr_data,
    output                   data_wr_rdy,
    output                   data_read_fault,
    output                   data_write_fault
/* *********** DCache Interface *********** */

);

    localparam DATA_ID = 4'h1;
    localparam INST_ID = 4'h0;

    localparam WRITE_IDLE = 3'b001;
    localparam WRITE_WAIT = 3'b010;
    localparam WRITE_DONE = 3'b100;

    localparam  AXI_BURST_INCR    = 2'd1;
/* ====================== Write ====================== */
    assign      io_master_awburst = AXI_BURST_INCR;
    assign      io_master_awid    = 0;

    wire        write_line_valid    = (data_wr_type == 3'b100);
    wire [7:0]  write_len           = write_line_valid ? 8'h1 : 8'h0;
    wire [2:0]  write_size          = write_line_valid ? 3'b011 : data_wr_type;

    reg                     axi_awvalid_R;
    reg [7:0]               axi_awlen_R;
    reg [2:0]               axi_awsize_R;
    reg [ADDR_WIDTH-1:0]    axi_awaddr_R;
    reg                     axi_wvalid_R;
    reg [DATA_WIDTH-1:0]    axi_wdata_R;
    reg [MAKS_WIDTH-1:0]    axi_wstrb_R;
    reg                     axi_wlast_R;
    reg                     axi_bready_R;

    reg [2:0]               write_state;
    reg [63:0]              write_buffer_data;
    reg [7:0]               write_buffer_strb;

    assign     io_master_awvalid = axi_awvalid_R;
    assign     io_master_awlen   = axi_awlen_R;
    assign     io_master_awsize  = axi_awsize_R;
    assign     io_master_awaddr  = axi_awaddr_R;
    assign     io_master_wvalid  = axi_wvalid_R;
    assign     io_master_wdata   = axi_wdata_R;
    assign     io_master_wstrb   = axi_wstrb_R;
    assign     io_master_wlast   = axi_wlast_R;
    assign     io_master_bready  = axi_bready_R;

    assign      inst_wr_rdy = 1'b1;
    assign      data_wr_rdy = (write_state == WRITE_IDLE);

    always @(posedge clock) begin
        if (reset) begin
            write_state         <= WRITE_IDLE;

            axi_awvalid_R       <= 0;
            axi_awlen_R         <= 0;
            axi_awsize_R        <= 0;
            axi_awaddr_R        <= 0;
            axi_wvalid_R        <= 0;
            axi_wdata_R         <= 0;
            axi_wstrb_R         <= 0;
            axi_wlast_R         <= 0;
            axi_bready_R        <= 0;

            write_buffer_data   <= 0;
            write_buffer_strb   <= 0;
        end
        else begin
            case (write_state)
                WRITE_IDLE: begin
                    if (data_wr_req) begin
                        write_state     <= WRITE_WAIT;

                        axi_awvalid_R       <= 1'b1;
                        axi_awlen_R         <= write_len;
                        axi_awsize_R        <= write_size;
                        axi_awaddr_R        <= data_wr_addr;
                        axi_wvalid_R        <= 1'b1;
                        axi_wdata_R         <= data_wr_data[63:0];
                        axi_wstrb_R         <= data_wr_wstrb[7:0];
                        axi_bready_R        <= 0;

                        if (write_line_valid)   axi_wlast_R   <= 1'b0;
                        else                    axi_wlast_R   <= 1'b1;

                        write_buffer_data   <= data_wr_data[127:64];
                        write_buffer_strb   <= data_wr_wstrb[15:8];
                    end
                end
                WRITE_WAIT: begin
                    if (io_master_awvalid & io_master_awready) begin
                        axi_awvalid_R       <= 0;
                    end
                    if (io_master_wvalid & io_master_wready) begin
                        if (~io_master_wlast) begin // Cache 第一拍
                            axi_wdata_R     <=  write_buffer_data;
                            axi_wstrb_R     <=  write_buffer_strb;
                            axi_wlast_R     <=  1'b1;
                        end
                        else begin // Uncache 或 Cache 最后一拍
                            write_state     <= WRITE_DONE;
                            axi_wvalid_R    <= 0;
                            axi_wlast_R     <= 0; /// !!!
                            axi_bready_R    <= 1'b1;
                        end
                    end
                end
                WRITE_DONE: begin
                    if (io_master_bready & io_master_bvalid) begin
                        write_state <= WRITE_IDLE;
                        axi_wdata_R     <= 0;
                        axi_wstrb_R     <= 0;
                        axi_bready_R    <= 1'b0;
                    end
                end
                default: begin
                    write_state <= WRITE_IDLE;
                end
            endcase
        end
    end

    wire    wait_write = ~(write_state == WRITE_IDLE);
/* ====================== Write ====================== */


/* ====================== Read ====================== */
    assign  io_master_arburst   = AXI_BURST_INCR;
    assign  io_master_arid      = 0;

    localparam  READ_IDLE = 3'b001;
    localparam  READ_WAIT = 3'b010;

    wire read_data  = data_rd_req;
    wire read_inst  = inst_rd_req & ~read_data;
    wire read_req   = read_data | read_inst;


    reg [2:0]               read_state;

    reg [DATA_WIDTH-1:0]    read_buffer_data;
    reg                     read_line_valid;

    wire no_wait_write  = (wait_write & io_master_bvalid & io_master_bready) | (~wait_write);
    wire read_rdy       = (read_state == READ_IDLE) & no_wait_write;

    assign data_rd_rdy  = read_rdy;
    assign inst_rd_rdy  = read_rdy & ~read_data;

    wire [2:0] read_type_array [1:0];
    assign read_type_array[0]   = inst_rd_type;
    assign read_type_array[1]   = data_rd_type;

    wire [31:0] read_addr_array [1:0];
    assign read_addr_array[0]   = inst_rd_addr;
    assign read_addr_array[1]   = data_rd_addr;

    wire [3:0]      read_id     = read_data ? DATA_ID : INST_ID;
    wire            read_line   = read_type_array[read_id[0]] == 3'b100;
    wire [31:0]     read_addr   = read_addr_array[read_id[0]];
    wire [7:0]      read_len    = read_line ? 8'h1 : 8'h0;
    wire [2:0]      read_size   = read_line ? 3'b011 : read_type_array[read_id[0]];


    reg [3:0]               read_id_R;
    reg [ADDR_WIDTH-1:0]    axi_araddr_R;
    reg [2:0]               axi_arsize_R;
    reg [7:0]               axi_arlen_R;
    reg                     axi_arvalid_R;
    reg                     axi_rready_R;

    assign  io_master_araddr  = axi_araddr_R;
    assign  io_master_arsize  = axi_arsize_R;
    assign  io_master_arlen   = axi_arlen_R;
    assign  io_master_arvalid = axi_arvalid_R;
    assign  io_master_rready = axi_rready_R;


    always @(posedge clock) begin
        if (reset) begin
            read_state          <= READ_IDLE;
            read_id_R           <= 0;
            axi_araddr_R        <= 0;
            axi_arsize_R        <= 0;
            axi_arlen_R         <= 0;
            axi_arvalid_R       <= 0;
            axi_rready_R        <= 1'b1;
            read_line_valid     <= 0;
            read_buffer_data    <= 0;
        end
        else begin
            case (read_state)
                READ_IDLE: begin
                    axi_araddr_R    <= read_addr;
                    axi_arsize_R    <= read_size;
                    axi_arlen_R     <= read_len;
                    read_line_valid <= 1'b0;

                    if (read_req & no_wait_write) begin
                        read_state      <= READ_WAIT;
                        read_id_R       <= read_id;
                        axi_arvalid_R   <= 1'b1;
                    end
                end
                READ_WAIT: begin
                    if (io_master_arvalid & io_master_arready) begin
                        axi_arvalid_R   <= 1'b0;
                    end
                    if (io_master_rready & io_master_rvalid) begin
                        // Cache First
                        if (~io_master_rlast) begin
                            read_state          <= READ_WAIT;
                            read_line_valid     <=  1'b1;
                            read_buffer_data    <=  io_master_rdata;
                        end
                        // Uncache or Cache Last
                        else begin
                            read_state          <= READ_IDLE;
                            read_line_valid     <= 1'b0;
                        end
                    end
                end
                default: begin
                    read_state <= READ_IDLE;
                end
            endcase
        end
    end

    wire [LINE_WIDTH-1:0] ret_data  = read_line_valid
                                    ? { io_master_rdata, read_buffer_data }
                                    : { 64'h0, io_master_rdata };
    assign  inst_ret_data = ret_data;
    assign  data_ret_data = ret_data;

    wire    is_inst_id      = (read_id_R == INST_ID);
    wire    is_data_id      = (read_id_R == DATA_ID);
    assign  inst_ret_valid  = is_inst_id & io_master_rvalid;
    assign  data_ret_valid  = is_data_id & io_master_rvalid;
    assign  inst_ret_last   = is_inst_id & io_master_rlast;
    assign  data_ret_last   = is_data_id & io_master_rlast;

    wire    rresp_err       = io_master_rvalid & (io_master_rresp[1]);
    assign  inst_read_fault = is_inst_id & rresp_err;
    assign  data_read_fault = is_data_id & rresp_err;
    wire    bresp_err       = io_master_bvalid & (io_master_bresp[1]);
    assign  inst_write_fault    = 1'b0;
    assign  data_write_fault    = is_data_id & bresp_err;
/* ====================== Read ====================== */

endmodule //AXI4Bridge


module ysyx_22050293_Cache #(
    parameter ADDR_WIDTH    = 32,
    parameter DATA_WIDTH    = 64,
    parameter MASK_WIDTH    = DATA_WIDTH / 8,

    parameter OFFSET_WIDTH  = 4,
    parameter INDEX_WIDTH   = 7,
    parameter TAG_WIDTH     = 21,

    parameter LINE_WIDTH    = 128,
    parameter STRB_WIDTH    = LINE_WIDTH / 8
) (
    input                       clock,
    input                       reset,

/* ********* Cache <-> SRAM ********* */
    // SRAM 0
    output [5:0]                sram0_addr,
    output                      sram0_cen,
    output                      sram0_wen,
    output [127:0]              sram0_wmask,
    output [127:0]              sram0_wdata,
    input  [127:0]              sram0_rdata,
    // SRAM 1
    output [5:0]                sram1_addr,
    output                      sram1_cen,
    output                      sram1_wen,
    output [127:0]              sram1_wmask,
    output [127:0]              sram1_wdata,
    input  [127:0]              sram1_rdata,
    // SRAM 2
    output [5:0]                sram2_addr,
    output                      sram2_cen,
    output                      sram2_wen,
    output [127:0]              sram2_wmask,
    output [127:0]              sram2_wdata,
    input  [127:0]              sram2_rdata,
    // SRAM 3
    output [5:0]                sram3_addr,
    output                      sram3_cen,
    output                      sram3_wen,
    output [127:0]              sram3_wmask,
    output [127:0]              sram3_wdata,
    input  [127:0]              sram3_rdata,
/* ********* Cache <-> SRAM ********* */

/* ********* Cache <-> CPU ********* */
    input                       request,
    input                       op,     // 1: WRITE, 0: READ1
    input  [TAG_WIDTH-1:0]      tag,
    input  [INDEX_WIDTH-1:0]    index,
    input  [OFFSET_WIDTH-1:0]   offset,
    input  [MASK_WIDTH-1:0]     wstrb,
    input  [DATA_WIDTH-1:0]     wdata,
    output                      addr_ok,
    output                      data_ok,
    output [DATA_WIDTH-1:0]     rdata,
    input  [2:0]                size,
    input                       uncache,
    output                      load_fault,
    output                      store_fault,
    // fence.i
    input                       flush,
    input                       clear,
    output                      done,
    output                      idle,
/* ********* Cache <-> CPU ********* */

/* ********* Cache <-> AXI ********* */
    // Read
    output                      rd_req,
    output [2:0]                rd_type,
    output [ADDR_WIDTH-1:0]     rd_addr,
    input                       rd_rdy,
    input                       ret_valid,
    input                       ret_last,
    input  [LINE_WIDTH-1:0]     ret_data,
    input                       read_fault,

    // Write
    output                      wr_req,
    output [2:0]                wr_type,
    output [ADDR_WIDTH-1:0]     wr_addr,
    output [STRB_WIDTH-1:0]     wr_wstrb,
    output [LINE_WIDTH-1:0]     wr_data,
    input                       wr_rdy,
    input                       write_fault
/* ********* Cache <-> AXI ********* */
);

    localparam NR_LINES = 1 << INDEX_WIDTH;

    // Port Registers
    reg [TAG_WIDTH-1:0]     tag_R;
    reg [INDEX_WIDTH-1:0]   index_R;
    reg [OFFSET_WIDTH-1:0]  offset_R;
    reg                     op_R;
    reg [2:0]               size_R;
    reg                     uncache_R;
    reg [MASK_WIDTH-1:0]    wstrb_R;
    reg [DATA_WIDTH-1:0]    wdata_R;

    // Flush Registers
    reg                     flush_flag_R;
    reg                     flush_done_R;
    reg [INDEX_WIDTH:0]     flush_line_R;
    reg                     flush_way_R;

    // Tag Array
    reg [LINE_WIDTH-1:0] [TAG_WIDTH-1:0]    tag_arr0;
    reg [LINE_WIDTH-1:0] [TAG_WIDTH-1:0]    tag_arr1;
    // Valid Array
    reg [LINE_WIDTH-1:0]                    valid_arr0;
    reg [LINE_WIDTH-1:0]                    valid_arr1;
    // Dirty Array
    reg [LINE_WIDTH-1:0]                    dirty_arr0;
    reg [LINE_WIDTH-1:0]                    dirty_arr1;

    // AXI Request
    reg wr_req_R;
    reg rd_req_R;

    wire cache_hit_read;

    // State
    reg [4:0] state;
    localparam IDLE     = 5'b00001;
    localparam QUERY    = 5'b00010;
    localparam WRITE    = 5'b00100;
    localparam READ     = 5'b01000;
    localparam REFILL   = 5'b10000;
    wire state_is_idle      = (state == IDLE);
    wire state_is_query     = (state == QUERY);
    // wire state_is_write     = (state == WRITE);
    wire state_is_read      = (state == READ);
    wire state_is_refill    = (state == REFILL);
    wire can_recv_req       ;
    wire recv_new_req       ;
    wire state_refill2idle  ;
    wire need_refill        ;


/* ------------------------ LFSR ------------------------ */
    reg [2:0] lfsr;
    always @(posedge clock) begin
        if (reset) begin
            lfsr <= 3'b111;
        end
        else if (state_refill2idle) begin
            lfsr <= { lfsr[0], lfsr[2] ^ lfsr[0], lfsr[1] };
        end
    end
    wire lfsr_way = lfsr[0];
/* ------------------------ LFSR ------------------------ */


/* ------------------------ Way Information ------------------------ */
    wire [6:0] real_index = ({7{flush_flag_R}} & flush_line_R[6:0]) | index_R;

    // Way Tag
    wire way0_hit = (tag_R == tag_arr0[index_R]) & (valid_arr0[index_R]) & (~uncache_R);
    wire way1_hit = (tag_R == tag_arr1[index_R]) & (valid_arr1[index_R]) & (~uncache_R);
    wire cache_hit          = way0_hit | way1_hit;
    wire cache_miss         = (~cache_hit) & (~uncache_R);
    wire cache_hit_write    = cache_hit & op_R;
    assign cache_hit_read   = cache_hit & (~op_R);

    // Way Data
    wire [LINE_WIDTH-1:0] way0_bank0_rdata = sram0_rdata;
    wire [LINE_WIDTH-1:0] way0_bank1_rdata = sram1_rdata;
    wire [LINE_WIDTH-1:0] way1_bank0_rdata = sram2_rdata;
    wire [LINE_WIDTH-1:0] way1_bank1_rdata = sram3_rdata;
    wire [LINE_WIDTH-1:0] way0_rdata    = (real_index[6]) ? way0_bank1_rdata : way0_bank0_rdata;
    wire [LINE_WIDTH-1:0] way1_rdata    = (real_index[6]) ? way1_bank1_rdata : way1_bank0_rdata;

    // Way Dirty & Valid & Tag
    wire                way0_dirty  = dirty_arr0[real_index];
    wire                way1_dirty  = dirty_arr1[real_index];
    wire                way0_valid  = valid_arr0[real_index];
    wire                way1_valid  = valid_arr1[real_index];
    wire [TAG_WIDTH-1:0] way0_tag   = tag_arr0[real_index];
    wire [TAG_WIDTH-1:0] way1_tag   = tag_arr1[real_index];

    // Way Replace
    reg [LINE_WIDTH-1:0]    replace_data_R;
    wire replace_way    =   flush_flag_R ? flush_way_R :
                            (way0_valid ^ way1_valid) ? (way0_valid) :
                            (way0_dirty ^ way1_dirty) ? (way0_dirty) :
                            lfsr_way;
    wire [TAG_WIDTH-1:0]  replace_tag    = replace_way ? way1_tag   : way0_tag  ;
    wire                  replace_dirty  = replace_way ? way1_dirty : way0_dirty;
    wire                  replace_valid  = replace_way ? way1_valid : way0_valid;
    wire [LINE_WIDTH-1:0] replace_data   = replace_way ? way1_rdata : way0_rdata;

/* ------------------------ Way Information ------------------------ */


/* ------------------- Cache <-> AXI ------------------- */
    assign wr_req   = wr_req_R;
    assign wr_type  = uncache_R ? size_R : 3'b100;
    assign wr_addr  = ({32{ uncache_R}} & {tag_R, index_R, offset_R})
                    | ({32{~uncache_R}} & {replace_tag, real_index, 4'h0});
    assign wr_wstrb = uncache_R ? {8'h0, wstrb_R} : 16'hFFFF;
    assign wr_data  = ({128{ uncache_R}} & {64'h0, wdata_R})
                    | ({128{~uncache_R}} & {replace_data_R});


    assign rd_req   = state_is_read;
    assign rd_type  = uncache_R ? size_R : 3'b100;
    assign rd_addr  = { tag_R, index_R, uncache_R ? offset_R : 4'h0 };
/* ------------------- Cache <-> AXI ------------------- */


/* ------------------- Cache <-> CPU ------------------- */
    assign addr_ok  = can_recv_req;
    assign data_ok  = (state_is_query & cache_hit)
                    | (state_refill2idle)
                    | (write_fault | read_fault);
    // Hit: Load Data From SRAM
    wire [LINE_WIDTH-1:0] load_line = ({128{way0_hit}} & way0_rdata)
                                    | ({128{way1_hit}} & way1_rdata);
    wire [DATA_WIDTH-1:0] load_data = offset_R[3] ? load_line[127:64] : load_line[63:0];
    // Read Data Through AXI
    wire [DATA_WIDTH-1:0] read_data = (uncache_R | ~offset_R[3]) ? ret_data[63:0] : ret_data[127:64];
    // Output Data
    assign rdata    = ({64{state_is_query }} & load_data)
                    | ({64{state_is_refill}} & read_data);
/* ------------------- Cache <-> CPU ------------------- */


/* ------------------------ State Machine ------------------------ */
    assign can_recv_req       = state_is_idle | state_is_query & cache_hit_read;
    assign recv_new_req       = can_recv_req & request & ~flush_flag_R;
    assign state_refill2idle  = state_is_refill & ((ret_valid & ret_last) | (~rd_req_R));
    assign need_refill        = state_refill2idle & ~uncache_R & ~flush_flag_R;

    always @(posedge clock) begin
        if (reset) begin
            state <= IDLE;
            { tag_R, index_R, offset_R }    <= 0;
            { op_R, size_R, uncache_R }     <= 0;
            { wstrb_R, wdata_R }            <= 0;
            { flush_flag_R, flush_done_R }  <= 0;
            { flush_line_R, flush_way_R }   <= 0;
            { wr_req_R, rd_req_R }          <= 0;
            replace_data_R                  <= 0;
        end
        else begin
            case (state)
                IDLE: begin
                    wr_req_R <= 1'b0;
                    // Flush
                    if (flush) begin
                        state <= QUERY;
                        { flush_flag_R, flush_done_R }  <= { 1'b1, 1'b0 };
                        { flush_line_R, flush_way_R  }  <= 0;
                        uncache_R <= 1'b0;
                    end
                    // Request
                    else if (request) begin
                        state <= QUERY;
                        { tag_R, index_R, offset_R }    <= { tag, index, offset };
                        { op_R,  size_R, uncache_R }    <= { op, size, uncache };
                        { wstrb_R, wdata_R }            <= { wstrb, wdata };
                        { flush_flag_R, flush_done_R }  <= { 1'b0, 1'b0 };
                    end
                    else begin
                        state <= IDLE;
                        { tag_R, index_R, offset_R }    <= 0;
                        { op_R,  size_R, uncache_R }    <= 0;
                        { wstrb_R, wdata_R }            <= 0;
                    end
                end
                QUERY: begin
                    // Flush
                    if (flush_flag_R) begin
                        // Continue Flush
                        if (flush_line_R != NR_LINES) begin
                            state <= WRITE;
                        end
                        // Finish Flush
                        else begin
                            state <= IDLE;
                            { flush_flag_R, flush_done_R }  <= { 1'b0, 1'b1 };
                        end
                    end
                    // Cache Hit
                    else if (cache_hit) begin
                        // Request and Hit Read
                        if (request & cache_hit_read) begin
                            state <= QUERY;
                            { tag_R, index_R, offset_R }    <= { tag, index, offset };
                            { op_R,  size_R, uncache_R }    <= { op, size, uncache };
                            { wstrb_R, wdata_R }            <= { wstrb, wdata };
                        end
                        // No Request or Hit Write
                        else begin
                            state <= IDLE;
                            // Don't Change Port Registers
                        end
                    end
                    // Cache Miss
                    else if (cache_miss) begin
                        // Cache Miss and Need Replace
                        if (replace_valid & replace_dirty) begin
                            state <= WRITE;
                        end
                        // Cache Miss and No Need Replace
                        else begin
                            state <= READ;
                        end
                        // Save Replace Information
                        replace_data_R  <= replace_data;
                    end
                    // Uncache
                    else if (uncache_R) begin
                        // Uncache Write
                        if (op_R) begin
                            state <= WRITE;
                        end
                        // Uncache Read
                        else begin
                            state <= READ;
                        end
                        // Save Replace Information
                        replace_data_R  <= replace_data;
                    end
                    else begin
                        state <= IDLE;
                    end
                end
                WRITE: begin
                    // Flush
                    if (flush_flag_R) begin
                        // Need Write Dirty Line
                        if (replace_valid & replace_dirty) begin
                            // Ready to Write Dirty Line
                            if (wr_rdy) begin
                                wr_req_R        <= 1'b1;
                                state           <= REFILL;
                                replace_data_R  <= replace_data;
                            end
                            // Wait for Write Dirty Line
                            else begin
                                wr_req_R        <= 1'b0;
                                state           <= WRITE;
                            end
                        end
                        // Not Write Dirty Line
                        else begin
                            state <= REFILL;
                        end
                    end
                    // Open Write Reuqest
                    else if (wr_rdy) begin
                        wr_req_R <= 1'b1;
                        // Jump to Refill State (Uncahce Write Not Need AXI Read)
                        if (uncache_R & op_R) begin
                            state <= REFILL;
                        end
                        // Jump to Read State
                        else begin
                            state <= READ;
                        end
                    end
                    else begin
                        state <= WRITE;
                    end
                end
                READ: begin
                    // Close Write Request
                    wr_req_R <= 1'b0;
                    // Write Fault
                    if (write_fault) begin
                        state <= IDLE;
                    end
                    // Read Request
                    else if (rd_rdy) begin
                        state <= REFILL;
                        // Need Read Request
                        rd_req_R <= 1'b1;
                    end
                    else begin
                        state <= READ;
                    end
                end
                REFILL: begin
                    // Close Write Request
                    wr_req_R <= 1'b0;
                    // Read Fault
                    if (read_fault) begin
                        state <= IDLE;
                    end
                    // Flush
                    else if (flush_flag_R) begin
                        state <= QUERY;
                        // Close Read Request
                        wr_req_R <= 1'b0;
                        // Update Flush Line, Way
                        { flush_line_R, flush_way_R } <= { flush_line_R, flush_way_R } + 1;
                    end
                    // Read Done
                    else if (ret_valid & ret_last) begin
                        state <= IDLE;
                        // Read Request Already Closed
                        rd_req_R <= 1'b0;
                    end
                    // No Read (Uncahce Write Not Need AXI Read)
                    else if (~rd_req_R) begin
                        state <= IDLE;
                    end
                    else begin
                        state <= REFILL;
                    end
                end
                default: begin
                    state <= IDLE;
                end
            endcase
        end
    end
/* ------------------------ State Machine ------------------------ */


/* -------------------- Cache <-> SRAM -------------------- */
    wire hit_write_sram = cache_hit_write & state_is_query;

    // SRAM wmask
    wire [63:0] temp_mask =
    {
        {8{wstrb_R[7]}}, {8{wstrb_R[6]}}, {8{wstrb_R[5]}}, {8{wstrb_R[4]}},
        {8{wstrb_R[3]}}, {8{wstrb_R[2]}}, {8{wstrb_R[1]}}, {8{wstrb_R[0]}}
    };
    wire [127:0] wmask_ext = offset_R[3] ? {temp_mask, 64'h0} : {64'h0, temp_mask};
    wire [127:0] wmask_all = {128{1'b1}};
    wire [127:0] sram_wmask = ({128{ hit_write_sram  }} & wmask_ext)
                            | ({128{ state_is_refill }} & wmask_all);

    // SRAM addr
    wire [5:0] sram_addr    = ({6{ recv_new_req    }} & index[5:0])         // Read
                            | ({6{ flush_flag_R    }} & flush_line_R[5:0])  // Read
                            | ({6{ hit_write_sram  }} & index_R[5:0])       // Write
                            | ({6{ state_is_refill }} & index_R[5:0]);      // Write

    // SRAM wdata
    // Extend wdata to 128 bits
    wire [127:0] wdata_ext  = offset_R[3] ? {wdata_R, 64'h0} : {64'h0, wdata_R};
    // Mix wdata and ret_data
    wire [127:0] wdata_mix  = (wdata_ext & wmask_ext) | (ret_data & ~wmask_ext);
    wire [127:0] wdata_all  = ({128{ op_R}} & wdata_mix)
                            | ({128{~op_R}} & ret_data );
    // Don't write SRAM in Write Dirty Line
    wire [127:0] sram_wdata = ({128{ hit_write_sram  }} & wdata_ext)
                            | ({128{ state_is_refill }} & wdata_all);

    /* ===================== Way0 ===================== */
    /////////////////////// Blank0 ///////////////////////
    wire way0_bank0_ren = (recv_new_req & ~index[6])
                        | (flush_flag_R & ~flush_line_R[6]);
    // Don't write SRAM in Write Dirty Line
    wire way0_bank0_wen = (hit_write_sram  & ~index_R[6] & way0_hit) // hit write
                        | (need_refill     & ~index_R[6] & ~replace_way);
    wire way0_bank0_cen = way0_bank0_ren | way0_bank0_wen;

    assign sram0_cen    = ~way0_bank0_cen;
    assign sram0_wen    = ~way0_bank0_wen;
    assign sram0_addr   = sram_addr;
    assign sram0_wmask  = ~sram_wmask;
    assign sram0_wdata  = sram_wdata;
    /////////////////////// Blank0 ///////////////////////

    /////////////////////// Blank1 ///////////////////////
    wire way0_bank1_ren = (recv_new_req &  index[6])
                        | (flush_flag_R &  flush_line_R[6]);
    // Don't write SRAM in Write Dirty Line
    wire way0_bank1_wen = (hit_write_sram  &  index_R[6] & way0_hit) // hit write
                        | (need_refill     &  index_R[6] & ~replace_way);
    wire way0_bank1_cen = way0_bank1_ren | way0_bank1_wen;

    assign sram1_cen  = ~way0_bank1_cen;
    assign sram1_wen  = ~way0_bank1_wen;
    assign sram1_addr = sram_addr;
    assign sram1_wmask = ~sram_wmask;
    assign sram1_wdata = sram_wdata;
    /////////////////////// Blank1 ///////////////////////
    /* ===================== Way0 ===================== */


    /* ===================== Way1 ===================== */
    /////////////////////// Blank0 ///////////////////////
    wire way1_bank0_ren = (recv_new_req & ~index[6])
                        | (flush_flag_R & ~flush_line_R[6]);
    // Don't write SRAM in Write Dirty Line
    wire way1_bank0_wen = (hit_write_sram  & ~index_R[6] & way1_hit) // hit write
                        | (need_refill     & ~index_R[6] & replace_way);
    wire way1_bank0_cen = way1_bank0_ren | way1_bank0_wen;

    assign sram2_cen    = ~way1_bank0_cen;
    assign sram2_wen    = ~way1_bank0_wen;
    assign sram2_addr   = sram_addr;
    assign sram2_wmask  = ~sram_wmask;
    assign sram2_wdata  = sram_wdata;
    /////////////////////// Blank0 ///////////////////////

    /////////////////////// Blank1 ///////////////////////
    wire way1_bank1_ren = (recv_new_req &  index[6])
                        | (flush_flag_R &  flush_line_R[6]);
    // Don't write SRAM in Write Dirty Line
    wire way1_bank1_wen = (hit_write_sram  &  index_R[6] & way1_hit) // hit write
                        | (need_refill     &  index_R[6] & replace_way);
    wire way1_bank1_cen = way1_bank1_ren | way1_bank1_wen;

    assign sram3_cen    = ~way1_bank1_cen;
    assign sram3_wen    = ~way1_bank1_wen;
    assign sram3_addr   = sram_addr;
    assign sram3_wmask  = ~sram_wmask;
    assign sram3_wdata  = sram_wdata;
    /////////////////////// Blank1 ///////////////////////
    /* ===================== Way1 ===================== */

/* -------------------- Cache <-> SRAM -------------------- */


/* ------------------------ Array ------------------------ */
    // Tag Array
    always @(posedge clock) begin
        if (reset) begin
            tag_arr0 <= 0;
            tag_arr1 <= 0;
        end
        else if (need_refill) begin
            if (replace_way)    tag_arr1[index_R] <= tag_R;
            else                tag_arr0[index_R] <= tag_R;
        end
    end

    // Valid Array
    always @(posedge clock) begin
        if (reset) begin
            valid_arr0 <= 0;
            valid_arr1 <= 0;
        end
        else if (state_is_idle & clear) begin
            valid_arr0 <= 0;
            valid_arr1 <= 0;
        end
        else if (state_is_query & flush_flag_R & flush_line_R == NR_LINES) begin
            valid_arr0 <= 0;
            valid_arr1 <= 0;
        end
        else if (need_refill) begin
            if (replace_way)    valid_arr1[index_R] <= 1'b1;
            else                valid_arr0[index_R] <= 1'b1;
        end
    end

    // Dirty Array
    always @(posedge clock) begin
        if (reset) begin
            dirty_arr0 <= 0;
            dirty_arr1 <= 0;
        end
        else if (state_is_query & flush_flag_R & flush_line_R == NR_LINES) begin
            dirty_arr0 <= 0;
            dirty_arr1 <= 0;
        end
        else if (hit_write_sram) begin
            if (way0_hit)  dirty_arr0[index_R] <= 1'b1;
            if (way1_hit)  dirty_arr1[index_R] <= 1'b1;
        end
        else if (need_refill & op_R) begin
            if (replace_way)    dirty_arr1[index_R] <= 1'b1;
            else                dirty_arr0[index_R] <= 1'b1;
        end
    end
/* ------------------------ Array ------------------------ */

    assign load_fault   = read_fault;
    assign store_fault  = write_fault;
    assign done         = flush_done_R;
    assign idle         = state_is_idle;

endmodule


module ysyx_22050293_CLINT (
    input           clock,
    input           reset,
    input           cen,
    input           wen,
    input  [31:0]   addr,
    input  [63:0]   wdata,
    output [63:0]   rdata,
    output          valid,
    output          fault,
    output [1:0]    intr
);

    wire req_msip       = (addr == 32'h0200_0000);
    wire req_mtime      = (addr == 32'h0200_BFF8);
    wire req_mtimecmp   = (addr == 32'h0200_4000);

    reg [31:0]  msip;
    reg [63:0]  mtime;
    reg [63:0]  mtimecmp;
    reg [64:0]  temp;

    reg [63:0]  rdata_R;
    reg         valid_R;
    reg         fault_R;
    assign rdata = rdata_R;
    assign valid = valid_R;
    assign fault = fault_R;

    always @(posedge clock) begin
        if (reset) begin
            msip        <= 0;
            mtime       <= 0;
            mtimecmp    <= 0;
            rdata_R     <= 0;
            valid_R     <= 0;
            temp        <= 0;
        end
        else begin
            temp  <= temp + 1;
            mtime <= temp[64:1];
            if (req_mtimecmp) begin
                fault_R <= 0;
                if (cen) begin
                    valid_R <= 1;
                    if (wen) mtimecmp <= wdata;
                    else     rdata_R  <= mtimecmp;
                end
                else begin
                    valid_R <= 0;
                end
            end
            else if (req_msip) begin
                fault_R <= 0;
                if (cen) begin
                    valid_R <= 1;
                    if (wen) msip    <= wdata[31:0];
                    else     rdata_R <= {32'h0, msip};
                end
                else begin
                    valid_R <= 0;
                end
            end
            else if (req_mtime) begin
                fault_R <= 0;
                if (cen) begin
                    valid_R  <= 1;
                    if (~wen) rdata_R <= mtime;
                end
                else begin
                    valid_R <= 0;
                end
            end
            else begin
                valid_R <= 0;
                if (cen) fault_R <= 1;
                else     fault_R <= 0;
            end
        end
    end

    assign intr =
    {
        mtime >= mtimecmp,  /* intr[1]: mtip */
        msip[0]             /* intr[0]: msip */
    };

endmodule


module ysyx_22050293_DCNode # (
    parameter ADDR_WIDTH = 32,

    parameter LINE_WIDTH    = 128,
    parameter STRB_WIDTH    = LINE_WIDTH / 8
) (
/* ********* DCNode <-> Cache ********* */
    // Read
    input                       m_rd_req,
    input  [2:0]                m_rd_type,
    input  [ADDR_WIDTH-1:0]     m_rd_addr,
    output                      m_rd_rdy,
    output                      m_ret_valid,
    output                      m_ret_last,
    output [LINE_WIDTH-1:0]     m_ret_data,
    output                      m_read_fault,

    // Write
    input                       m_wr_req,
    input  [2:0]                m_wr_type,
    input  [ADDR_WIDTH-1:0]     m_wr_addr,
    input  [STRB_WIDTH-1:0]     m_wr_wstrb,
    input  [LINE_WIDTH-1:0]     m_wr_data,
    output                      m_wr_rdy,
    output                      m_write_fault,
/* ********* DCNode <-> Cache ********* */

/* ********* DCNode <-> AXI ********* */
    // Read
    output                      s_rd_req,
    output [2:0]                s_rd_type,
    output [ADDR_WIDTH-1:0]     s_rd_addr,
    input                       s_rd_rdy,
    input                       s_ret_valid,
    input                       s_ret_last,
    input  [LINE_WIDTH-1:0]     s_ret_data,
    input                       s_read_fault,

    // Write
    output                      s_wr_req,
    output [2:0]                s_wr_type,
    output [ADDR_WIDTH-1:0]     s_wr_addr,
    output [STRB_WIDTH-1:0]     s_wr_wstrb,
    output [LINE_WIDTH-1:0]     s_wr_data,
    input                       s_wr_rdy,
    input                       s_write_fault,
/* ********* DCNode <-> AXI ********* */

/* ********* DCNode <-> CLINT ********* */
    output                      clint_cen,
    output                      clint_wen,
    output [31:0]               clint_addr,
    output [63:0]               clint_wdata,
    input  [63:0]               clint_rdata,
    input                       clint_valid,
    input                       clint_fault
/* ********* DCNode <-> CLINT ********* */
);

    wire rd_clint = m_rd_req & (m_rd_addr >= 32'h0200_0000 && m_rd_addr < 32'h0201_0000);
    wire wr_clint = m_wr_req & (m_wr_addr >= 32'h0200_0000 && m_wr_addr < 32'h0201_0000);

    wire req_clint = rd_clint | wr_clint;

    // CLINT
    assign clint_cen = req_clint;
    assign clint_wen = wr_clint;
    assign clint_addr = m_wr_addr[31:0];
    assign clint_wdata = m_wr_data[63:0];

    // Read
    assign s_rd_req = ~rd_clint & m_rd_req;
    assign s_rd_type = m_rd_type;
    assign s_rd_addr = m_rd_addr;
    assign m_rd_rdy = s_rd_rdy /*| clint_ready */;
    assign m_ret_valid = s_ret_valid | clint_valid;
    assign m_ret_last = s_ret_last | clint_valid;
    assign m_ret_data = clint_valid ? {64'h0, clint_rdata} : s_ret_data;
    assign m_read_fault = s_read_fault | clint_fault;

    // Write
    assign s_wr_req = ~wr_clint & m_wr_req;
    assign s_wr_type = m_wr_type;
    assign s_wr_addr = m_wr_addr;
    assign s_wr_wstrb = m_wr_wstrb;
    assign s_wr_data = req_clint ? {64'h0, clint_wdata} : m_wr_data;
    assign m_wr_rdy = s_wr_rdy /*| clint_ready */;
    assign m_write_fault = s_write_fault | clint_fault;

endmodule


module ysyx_22050293_Regfile (
    input            clock,
    input            reset,

    // Write Port
    input            we,
    input  [4:0]     waddr,
    input  [63:0]    wdata,

    // Read Port 1
    input  [4:0]     raddr1,
    output [63:0]    rdata1,

    // Read Port 2
    input  [4:0]     raddr2,
    output [63:0]    rdata2
);

    reg [31:0] [63:0]   gpr_array;

    // Write
    always @(posedge clock) begin
        if (reset) begin
            gpr_array           <= 0;
        end
        else if (we && waddr != 5'h0) begin
            gpr_array[waddr]    <= wdata;
        end
    end

    // Read
    assign rdata1 = gpr_array[raddr1];
    assign rdata2 = gpr_array[raddr2];

endmodule


module ysyx_22050293_CSR (
    input           clock,
    input           reset,

    input   [11:0]  csr_raddr,
    output  [63:0]  csr_rdata,

    // StageWB -> CSR
    input   [206:0] wb2csr_bus,

    // CSR -> All Stages
    output          csr_taken,
    // CSR -> StageIF
    output  [63:0]  csr_target,

    // CLINT -> CSR @ Interrupt
    input   [1:0]   clint_intr,

    // CSR -> StageID @ Interrupt
    output  [1:0]   csr_intr
);

    localparam CSR_MSTATUS_ADDR =   12'h300;
    localparam CSR_MIE_ADDR     =   12'h304;
    localparam CSR_MTVEC_ADDR   =   12'h305;
    localparam CSR_MEPC_ADDR    =   12'h341;
    localparam CSR_MCAUSE_ADDR  =   12'h342;
    localparam CSR_MIP_ADDR     =   12'h344;

    // CSR
    reg [63:0] mstatus;
    reg [63:0] mie;
    reg [63:0] mtvec;
    reg [63:0] mepc;
    reg [63:0] mcause;
    reg [63:0] mip;

    wire read_mstatus = (csr_raddr == CSR_MSTATUS_ADDR);
    wire read_mie     = (csr_raddr == CSR_MIE_ADDR);
    wire read_mtvec   = (csr_raddr == CSR_MTVEC_ADDR);
    wire read_mepc    = (csr_raddr == CSR_MEPC_ADDR);
    wire read_mcause  = (csr_raddr == CSR_MCAUSE_ADDR);
    wire read_mip     = (csr_raddr == CSR_MIP_ADDR);

    // Read CSR
    assign csr_rdata = ({64{read_mstatus}} & mstatus) |
                       ({64{read_mie}}     & mie)     |
                       ({64{read_mtvec}}   & mtvec)   |
                       ({64{read_mepc}}    & mepc)    |
                       ({64{read_mcause}}  & mcause)  |
                       ({64{read_mip}}     & mip)     ;

/* ---------------------------- StageWB -> CSR ---------------------------- */
    wire           csr_we;
    wire [11:0]    csr_waddr;
    wire [63:0]    csr_wdata;
    wire           mret_taken;
    wire           trap_taken;
    wire [63:0]    trap_cause;
    wire [63:0]    trap_pc;

    assign {
        csr_we,
        csr_waddr,
        csr_wdata,
        mret_taken,
        trap_taken,
        trap_cause,
        trap_pc
    } = wb2csr_bus;

    wire write_mstatus = csr_we & (csr_waddr == CSR_MSTATUS_ADDR);
    wire write_mie     = csr_we & (csr_waddr == CSR_MIE_ADDR);
    wire write_mtvec   = csr_we & (csr_waddr == CSR_MTVEC_ADDR);
    wire write_mepc    = csr_we & (csr_waddr == CSR_MEPC_ADDR);
    wire write_mcause  = csr_we & (csr_waddr == CSR_MCAUSE_ADDR);
    wire write_mip     = csr_we & (csr_waddr == CSR_MIP_ADDR);
/* ---------------------------- StageWB -> CSR ---------------------------- */


/* ------------------------------- MSTATUS ------------------------------- */
    always @ (posedge clock) begin
        if (reset) begin
            mstatus         <= 64'h000001800;
        end
        else if (trap_taken) begin
            mstatus[7]      <= mstatus[3];  // MPIE <= MIE
            mstatus[3]      <= 1'b0;        // MIE <= 0
            mstatus[12:11]  <= 2'b11;       // MPP <= 3
        end
        else if (mret_taken) begin
            mstatus[3]      <= mstatus[7];  // MIE <= MPIE
            mstatus[7]      <= 1'b1;        // MPIE <= 1
        end
        else if (write_mstatus) begin
            mstatus         <= csr_wdata;
        end
    end
/* ------------------------------- MSTATUS ------------------------------- */


/* ------------------------------- MIE ------------------------------- */
    always @ (posedge clock) begin
        if (reset) begin
            mie <= 64'h0;
        end
        else if (write_mie) begin
            mie <= csr_wdata;
        end
    end
/* ------------------------------- MIE ------------------------------- */


/* ------------------------------- MTVEC ------------------------------- */
    always @ (posedge clock) begin
        if (reset) begin
            mtvec <= 64'h0;
        end
        else if (write_mtvec) begin
            mtvec <= csr_wdata;
        end
    end
/* ------------------------------- MTVEC ------------------------------- */


/* ------------------------------- MEPC ------------------------------- */
    always @ (posedge clock) begin
        if (reset) begin
            mepc    <= 64'h0;
        end
        else if (trap_taken) begin
            mepc    <= trap_pc;
        end
        else if (write_mepc) begin
            mepc    <= csr_wdata;
        end
    end
/* ------------------------------- MEPC ------------------------------- */


/* ------------------------------- MCAUSE ------------------------------- */
    always @ (posedge clock) begin
        if (reset) begin
            mcause  <= 64'h0;
        end
        else if (trap_taken) begin
            mcause  <= trap_cause;
        end
        else if (write_mcause) begin
            mcause  <= csr_wdata;
        end
    end
/* ------------------------------- MCAUSE ------------------------------- */


/* -------------------------------- MIP -------------------------------- */
    wire clint_msi = clint_intr[0];
    wire clint_mti = clint_intr[1];

    always @ (posedge clock) begin
        if (reset) begin
            mip <= 64'h0;
        end
        // 可以标记中断
        else if ( (~trap_taken ) & (~mret_taken) & (~write_mstatus) & (~write_mie)
                | (~write_mtvec) & (~write_mepc) & (~write_mcause ) & (~write_mip) ) begin
            mip[ 3] <= clint_msi;
            mip[ 7] <= clint_mti;
            mip[11] <= 1'b0;
        end
    end
/* -------------------------------- MIP -------------------------------- */


/* ---------------------- CSR -> StageID @ Interrupt ---------------------- */
    assign csr_intr[0]  = mie[3] & mip[3] & mstatus[3];
    assign csr_intr[1]  = mie[7] & mip[7] & mstatus[3];
/* ---------------------- CSR -> StageID @ Interrupt ---------------------- */


/* ------------------------------ CSR -> All Stages ------------------------------ */
    assign csr_taken    = mret_taken | trap_taken;
/* ------------------------------ CSR -> All Stages ------------------------------ */

/* -------------------------------- CSR -> StageIF -------------------------------- */
    assign csr_target   = mret_taken ? mepc : mtvec ;
/* -------------------------------- CSR -> StageIF -------------------------------- */

endmodule


module ysyx_22050293_BranchJumpUnit (
    input  [63:0]    pc,
    input  [63:0]    rs1,
    input  [63:0]    rs2,
    input  [63:0]    imm,
    input  [7:0]     bj_info,
    output           bj_flag,
    output [63:0]    bj_target
);

/* ------------ Branch Jump Inst ------------ */
    wire inst_beq   = bj_info[0];
    wire inst_bne   = bj_info[1];
    wire inst_blt   = bj_info[2];
    wire inst_bge   = bj_info[3];
    wire inst_bltu  = bj_info[4];
    wire inst_bgeu  = bj_info[5];
    wire inst_jalr  = bj_info[6];
    wire inst_jal   = bj_info[7];
/* ------------ Branch Jump Inst ------------ */


/* ------------------------ Branch Jump Compare ------------------------ */
    wire [63:0]    bj_sum;
    wire           bj_cout;

    wire [63:0] not_rs2 = ~rs2;
    assign {bj_cout, bj_sum} = rs1 + not_rs2 + 1'b1;


    wire eq = (bj_cout) & (~|bj_sum);
    wire ne = ~eq;
    wire lt = (rs1[63]              & ~rs2[63]) |
              ((rs1[63] ~^ rs2[63]) & bj_sum[63]);
    wire ltu = ~bj_cout;
    wire ge  = ~lt;
    wire geu = bj_cout;
/* ------------------------ Branch Jump Compare ------------------------ */


    // Branch Jump Target
    assign bj_target = (inst_jalr ? rs1 : pc) + imm;

    // Branch Jump Flag
    assign bj_flag = (inst_beq  & eq )
                   | (inst_bne  & ne )
                   | (inst_blt  & lt )
                   | (inst_bge  & ge )
                   | (inst_bltu & ltu)
                   | (inst_bgeu & geu)
                   | (inst_jalr      )
                   | (inst_jal       );

endmodule


module ysyx_22050293_StageIF (
    input           clock,
    input           reset,

    // StageID -> StageIF
    input           id2if_allow,
    input   [65:0]  bj_bus,

    // CSR -> StageIF
    input           csr_taken,
    input  [63:0]   csr_target,

    // StageIF -> StageID
    output          if2id_fired,
    output [175:0]  if2id_bus,

    // StageID -> StageIF @ Cancel Flag
    input           id_cancel,
    // StageEX -> StageIF @ Cancel Flag
    input           ex_cancel,
    // StageMA -> StageIF @ Cancel Flag
    input           ma_cancel,
    // StageWB -> StageIF @ Cancel Flag
    input           wb_cancel,

    // StageID -> StageIF @ fence.i
    input           id_fencei_taken,
    input   [63:0]  id_fencei_target,

    /* ********* InstBus Interface ********* */
    output          ibus_req,
    output          ibus_op,
    output  [2:0]   ibus_size,
    output  [7:0]   ibus_mask,
    output  [31:0]  ibus_addr,
    output  [63:0]  ibus_wdata,
    input   [63:0]  ibus_rdata,
    input           ibus_addr_ok,
    input           ibus_data_ok,
    output          ibus_uncache,
    input           ibus_load_fault,
    /* lint_off UNUSEDSIGNAL */
    input           ibus_store_fault,
    output          ibus_flush,
    output          ibus_clear,
    input           ibus_done,
    input           ibus_idle
    /* ********* InstBus Interface ********* */
);

    localparam RESET_VECTOR =   64'h30000000;

    reg         if_valid;
    reg  [63:0] if_pc;
    wire [15:0] if_excp_flags;

    wire [63:0] next_pc;
    reg  [63:0] next_pc_buf;
    reg         next_pc_buf_valid;

    reg         ibus_addr_ack;
    reg [63:0]  inst_buf;
    reg         inst_buf_valid;
    reg         inst_discard_valid;


    // Select Inst
    wire [63:0] inst_data   = inst_buf_valid ? inst_buf : ibus_rdata;
    wire [31:0] if_inst     = if_pc[2] ? inst_data[63:32] : inst_data[31:0];


/* ************************* Branch & Jump ************************* */
    wire        bj_taken;
    wire        bj_stall;
    wire [63:0] bj_target;
    assign { bj_taken, bj_stall, bj_target } = bj_bus;
/* ************************* Branch & Jump ************************* */


/* ************************** Trap ************************** */
    // StageIF -> StageID
    wire inst_addr_misaligned = if_valid & (|next_pc[1:0]);
    wire inst_access_fault = if_valid & ibus_load_fault;

    assign if_excp_flags[0] = inst_addr_misaligned;
    assign if_excp_flags[1] = ~inst_addr_misaligned & inst_access_fault;
    assign if_excp_flags[15:2] = 0;

/* ----------------------------- Cancel Fetch ----------------------------- */
    wire if_cancel    = if_valid & (|if_excp_flags);
    wire cancel_fetch = if_cancel
                      | id_cancel
                      | ex_cancel
                      | ma_cancel
                      | wb_cancel;
/* ----------------------------- Cancel Fetch ----------------------------- */
/* ************************** Trap ************************** */


/* ************************* Pipeline ************************* */
/* -------------- StagePF <=> StageIF -------------- */
    wire        pf_ready    = ibus_req & ibus_addr_ok;
    wire        pf_valid    = 1'b1;
    wire        pf2if_fired = pf_ready & pf_valid;
    wire [63:0] pc_plus_4   = if_pc + 64'h4;
    assign next_pc = next_pc_buf_valid ? next_pc_buf
                   : csr_taken ? csr_target
                   : bj_taken  ? bj_target
                   : pc_plus_4 ;

    wire    if_ready    = (ibus_data_ok | inst_buf_valid) & (~inst_discard_valid);
    wire    if2pf_allow = (~if_valid) | (if_ready & id2if_allow);
    assign  if2id_fired = if_valid & if_ready;

    always @ (posedge clock) begin
        if (reset) begin
            if_valid <= 1'b0;
        end
        else if (if2pf_allow | csr_taken) begin
            if_valid <= pf2if_fired;
        end
        // fence.i
        else if (bj_taken | id_fencei_taken) begin
            if_valid <= 1'b0;
        end
    end

    always @ (posedge clock) begin
        if (reset) begin
            if_pc <= RESET_VECTOR - 4;
        end
        else if (pf2if_fired & if2pf_allow) begin
            if_pc <= next_pc;
        end
    end
/* -------------- StagePF <=> StageIF -------------- */

/* -------------- StageIF <=> StageID -------------- */
    assign if2id_bus = {
        // to StageID
        if_pc,
        if_inst,
        pc_plus_4,
        if_excp_flags
    };
/* -------------- StageIF <=> StageID -------------- */
/* ************************* Pipeline ************************* */

/* ************************* Fetch ************************* */
    // Addr Ack
    always @(posedge clock) begin
        if (reset) begin
            ibus_addr_ack <= 1'b0;
        end
        else if (ibus_req & ibus_addr_ok) begin
            ibus_addr_ack <= 1'b1;
        end
        else if (ibus_addr_ack & ibus_data_ok) begin
            ibus_addr_ack <= 1'b0;
        end
    end

    // Inst Buffer
    always @ (posedge clock) begin
        if (reset | csr_taken) begin
            inst_buf        <= 0;
            inst_buf_valid  <= 1'b0;
        end
        else if (ibus_data_ok & (~id2if_allow)) begin
            inst_buf        <= ibus_rdata;
            inst_buf_valid  <= 1'b1;
        end
        else if (inst_buf_valid & id2if_allow) begin
            inst_buf        <= 0;
            inst_buf_valid  <= 1'b0;
        end
    end

    // NextPC Buffer
    always @(posedge clock) begin
        if (reset) begin
            next_pc_buf         <= 0;
            next_pc_buf_valid   <= 1'b0;
        end
        else if (csr_taken & ~pf_ready) begin
            next_pc_buf         <= csr_target;
            next_pc_buf_valid   <= 1'b1;
        end
        else if (bj_taken & ~pf_ready) begin
            next_pc_buf         <= bj_target;
            next_pc_buf_valid   <= 1'b1;
        end
        else if (id_fencei_taken & ~pf_ready) begin
            next_pc_buf         <= id_fencei_target;
            next_pc_buf_valid   <= 1'b1;
        end
        else if (next_pc_buf_valid & pf_ready) begin
            next_pc_buf         <= 0;
            next_pc_buf_valid   <= 1'b0;
        end
    end

    // Inst Discard
    always @ (posedge clock) begin
        if (reset) begin
            inst_discard_valid <= 1'b0;
        end
        else if (csr_taken & (~if2pf_allow & ~if_ready)) begin
            inst_discard_valid <= 1'b1;
        end
        else if (inst_discard_valid & ibus_data_ok) begin
            inst_discard_valid <= 1'b0;
        end
    end

    // IBus
    assign ibus_req = (~reset)
                    & (if2pf_allow)
                    & (ibus_addr_ack & ibus_data_ok | ~ibus_addr_ack)
                    & (~bj_stall)
                    & (~cancel_fetch); // 其他流水级产生异常或者中断, 需要取消取指
    assign ibus_op    = 1'b0;
    assign ibus_size  = 3'h2;
    assign ibus_mask  = 8'b00000000;
    assign ibus_addr  = next_pc[31:0];
    assign ibus_wdata = 64'b0;
    assign ibus_uncache = ~(ibus_addr >= 32'h8000_0000 && ibus_addr < 32'hFC00_0000);
/* ************************* Fetch ************************* */

    // fence.i
    reg fencei_R;
    assign ibus_clear = fencei_R;
    assign ibus_flush = 0;
    always @(posedge clock) begin
        if (reset) begin
            fencei_R <= 1'b0;
        end
        else if (id_fencei_taken) begin
            fencei_R <= 1'b1;
        end
        else if (fencei_R & ibus_idle) begin
            fencei_R <= 1'b0;
        end
    end

endmodule


module ysyx_22050293_StageID (
    input           clock,
    input           reset,

    // StageID -> StageIF
    output          id2if_allow,
    output [65:0]   bj_bus,

    // StageIF -> StageID
    input           if2id_fired,
    input  [175:0]  if2id_bus,

    // StageID -> StageEX
    output          id2ex_valid,
    output [562:0]  id2ex_bus,

    // StageEX -> StageID
    input           ex2id_allow,

    // StageID -> Regfile
    output [9:0]    id2rf_bus,

    // Regfile -> StageID
    input  [127:0]  rf2id_bus,

    // StageID -> CSR
    output [11:0]   csr_raddr,

    // CSR -> StageID
    input   [63:0]  csr_rdata,

    // StageEX -> StageID @ Forward & Block
    input   [70:0]  ex_rf_hzd_bus,
    input   [12:0]  ex_csr_blk_bus,

    // StageMA -> StageID @ Forward & Block
    input   [70:0]  ma_rf_hzd_bus,
    input   [12:0]  ma_csr_blk_bus,

    // StageWB -> StageID @ Forward & Block
    input   [70:0]  wb_rf_hzd_bus,
    input   [12:0]  wb_csr_blk_bus,

    // StageID -> StageIF @ Cancel Flag
    output          id_cancel,

    // StageID -> StageIF @ fence.i
    output          id_fencei_taken,
    output [63:0]   id_fencei_target,

    // CSR -> StageID
    input           csr_taken,
    input  [1:0]    csr_intr // @ Interrupt
);

    // to StageID signal
    wire [63:0]     id_pc;
    wire [31:0]     id_inst;
    wire [63:0]     id_pc_plus_4;
    /* lint_off UNUSEDSIGNAL */
    wire [15:0]     if2id_excp_flags;

    wire [31:0]     inst;
    wire [63:0]     id_next_pc;

    reg [175:0]     if2id_bus_R;
    reg             id_valid;

    wire [7:0]      bj_info;
    wire            bj_flag;
    wire [63:0]     bj_target;
    wire            bj_taken;
    wire            bj_stall;
    wire            bj_stop;

    wire            csr_we;
    wire [11:0]     csr_waddr;
    wire [63:0]     csr_wdata;

    wire            rf_blk, ex_rf_blk, ma_rf_blk, wb_rf_blk;
    wire            ex_rs1_fwd, ex_rs2_fwd, ma_rs1_fwd, ma_rs2_fwd, wb_rs1_fwd, wb_rs2_fwd;
    wire [63:0]     real_rs1, real_rs2;

    wire            ex_csr_blk_flag, ma_csr_blk_flag, wb_csr_blk_flag;
    wire [11:0]     ex_csr_waddr, ma_csr_waddr, wb_csr_waddr;
    wire            ex_csr_blk, ma_csr_blk, wb_csr_blk, csr_blk;

    wire [15:0]     id_excp_flags;
    wire [15:0]     id_intr_flags;

    wire [11:0]     alu_op;
    wire            alu_word;
    wire            alu_unsign;
    wire [1:0]      alu_mul_signs;
    wire            alu_mul_high;
    wire [63:0]     alu_src1;
    wire [63:0]     alu_src2;

    wire [3:0]      mem_op;
    wire            mem_en;
    wire            mem_we;
    wire [2:0]      mem_size;
    wire            load_sext;
    wire [63:0]     mem_wdata;

    wire [2:0]      rf_sel;
    wire [63:0]     rf_rdata1;
    wire [63:0]     rf_rdata2;
    wire            rf_we;
    wire [4:0]      rf_waddr;

/* ************************* Decode ************************* */
    wire R_type, I_type, S_type, U_type, B_type, J_type;

    wire [6:0] opcode = inst[6:0];
    wire [2:0] funct3 = inst[14:12];
    wire [6:0] funct7 = inst[31:25];

    wire [4:0] rd  = inst[11:7];
    wire [4:0] rs1 = inst[19:15];
    wire [4:0] rs2 = inst[24:20];
    /* lint_off UNUSEDSIGNAL */
    wire [127:0]   opcodeD;
    wire [7:0]     funct3D;
    wire [127:0]   funct7D;

    wire shift_imm_arith = (inst[31:26] == 6'b010000);
    wire shift_imm_logic = (inst[31:26] == 6'b000000);

    ysyx_22050293_Decoder #(7, 128) opcode_decoder    ( .in (opcode), .out (opcodeD) );
    ysyx_22050293_Decoder #(3, 8  ) funct3_decoder    ( .in (funct3), .out (funct3D) );
    ysyx_22050293_Decoder #(7, 128) funct7_decoder    ( .in (funct7), .out (funct7D) );

    // RV32I
    wire inst_lui       = (opcodeD[7'b0110111]);
    wire inst_auipc     = (opcodeD[7'b0010111]);
    wire inst_jal       = (opcodeD[7'b1101111]);
    wire inst_jalr      = (opcodeD[7'b1100111]);
    wire inst_beq       = (opcodeD[7'b1100011] & funct3D[3'b000]);
    wire inst_bne       = (opcodeD[7'b1100011] & funct3D[3'b001]);
    wire inst_blt       = (opcodeD[7'b1100011] & funct3D[3'b100]);
    wire inst_bge       = (opcodeD[7'b1100011] & funct3D[3'b101]);
    wire inst_bltu      = (opcodeD[7'b1100011] & funct3D[3'b110]);
    wire inst_bgeu      = (opcodeD[7'b1100011] & funct3D[3'b111]);
    wire inst_lb        = (opcodeD[7'b0000011] & funct3D[3'b000]);
    wire inst_lh        = (opcodeD[7'b0000011] & funct3D[3'b001]);
    wire inst_lw        = (opcodeD[7'b0000011] & funct3D[3'b010]);
    wire inst_lbu       = (opcodeD[7'b0000011] & funct3D[3'b100]);
    wire inst_lhu       = (opcodeD[7'b0000011] & funct3D[3'b101]);
    wire inst_sb        = (opcodeD[7'b0100011] & funct3D[3'b000]);
    wire inst_sh        = (opcodeD[7'b0100011] & funct3D[3'b001]);
    wire inst_sw        = (opcodeD[7'b0100011] & funct3D[3'b010]);
    wire inst_addi      = (opcodeD[7'b0010011] & funct3D[3'b000]);
    wire inst_slti      = (opcodeD[7'b0010011] & funct3D[3'b010]);
    wire inst_sltiu     = (opcodeD[7'b0010011] & funct3D[3'b011]);
    wire inst_xori      = (opcodeD[7'b0010011] & funct3D[3'b100]);
    wire inst_ori       = (opcodeD[7'b0010011] & funct3D[3'b110]);
    wire inst_andi      = (opcodeD[7'b0010011] & funct3D[3'b111]);
    wire inst_slli      = (opcodeD[7'b0010011] & funct3D[3'b001] & shift_imm_logic);
    wire inst_srli      = (opcodeD[7'b0010011] & funct3D[3'b101] & shift_imm_logic);
    wire inst_srai      = (opcodeD[7'b0010011] & funct3D[3'b101] & shift_imm_arith);
    wire inst_add       = (opcodeD[7'b0110011] & funct3D[3'b000] & funct7D[7'b0000000]);
    wire inst_sub       = (opcodeD[7'b0110011] & funct3D[3'b000] & funct7D[7'b0100000]);
    wire inst_sll       = (opcodeD[7'b0110011] & funct3D[3'b001] & funct7D[7'b0000000]);
    wire inst_slt       = (opcodeD[7'b0110011] & funct3D[3'b010] & funct7D[7'b0000000]);
    wire inst_sltu      = (opcodeD[7'b0110011] & funct3D[3'b011] & funct7D[7'b0000000]);
    wire inst_xor       = (opcodeD[7'b0110011] & funct3D[3'b100] & funct7D[7'b0000000]);
    wire inst_srl       = (opcodeD[7'b0110011] & funct3D[3'b101] & funct7D[7'b0000000]);
    wire inst_sra       = (opcodeD[7'b0110011] & funct3D[3'b101] & funct7D[7'b0100000]);
    wire inst_or        = (opcodeD[7'b0110011] & funct3D[3'b110] & funct7D[7'b0000000]);
    wire inst_and       = (opcodeD[7'b0110011] & funct3D[3'b111] & funct7D[7'b0000000]);
    // wire inst_fence     = (opcodeD[7'b0001111] & funct3D[3'b000] & funct7D[7'b0000000]);
    wire inst_fence_i   = (opcodeD[7'b0001111] & funct3D[3'b001] & funct7D[7'b0000000]);
    wire inst_ecall     = (opcodeD[7'b1110011] & funct3D[3'b000] & funct7D[7'b0000000] & rd==0 & rs1==0 & rs2==0);
    wire inst_ebreak    = (opcodeD[7'b1110011] & funct3D[3'b000] & funct7D[7'b0000000] & rd==0 & rs1==0 & rs2==1);
    wire inst_csrrc     = (opcodeD[7'b1110011] & funct3D[3'b011]);
    wire inst_csrrs     = (opcodeD[7'b1110011] & funct3D[3'b010]);
    wire inst_csrrw     = (opcodeD[7'b1110011] & funct3D[3'b001]);
    wire inst_csrrci    = (opcodeD[7'b1110011] & funct3D[3'b111]);
    wire inst_csrrsi    = (opcodeD[7'b1110011] & funct3D[3'b110]);
    wire inst_csrrwi    = (opcodeD[7'b1110011] & funct3D[3'b101]);
    // RV32M
    wire inst_mul       = (opcodeD[7'b0110011] & funct3D[3'b000] & funct7D[7'b0000001]);
    wire inst_mulh      = (opcodeD[7'b0110011] & funct3D[3'b001] & funct7D[7'b0000001]);
    wire inst_mulhsu    = (opcodeD[7'b0110011] & funct3D[3'b010] & funct7D[7'b0000001]);
    wire inst_mulhu     = (opcodeD[7'b0110011] & funct3D[3'b011] & funct7D[7'b0000001]);
    wire inst_div       = (opcodeD[7'b0110011] & funct3D[3'b100] & funct7D[7'b0000001]);
    wire inst_divu      = (opcodeD[7'b0110011] & funct3D[3'b101] & funct7D[7'b0000001]);
    wire inst_rem       = (opcodeD[7'b0110011] & funct3D[3'b110] & funct7D[7'b0000001]);
    wire inst_remu      = (opcodeD[7'b0110011] & funct3D[3'b111] & funct7D[7'b0000001]);
    // RV64I
    wire inst_lwu       = (opcodeD[7'b0000011] & funct3D[3'b110]);
    wire inst_ld        = (opcodeD[7'b0000011] & funct3D[3'b011]);
    wire inst_sd        = (opcodeD[7'b0100011] & funct3D[3'b011]);
    wire inst_addiw     = (opcodeD[7'b0011011] & funct3D[3'b000]);
    wire inst_slliw     = (opcodeD[7'b0011011] & funct3D[3'b001] & shift_imm_logic);
    wire inst_srliw     = (opcodeD[7'b0011011] & funct3D[3'b101] & shift_imm_logic);
    wire inst_sraiw     = (opcodeD[7'b0011011] & funct3D[3'b101] & shift_imm_arith);
    wire inst_addw      = (opcodeD[7'b0111011] & funct3D[3'b000] & funct7D[7'b0000000]);
    wire inst_subw      = (opcodeD[7'b0111011] & funct3D[3'b000] & funct7D[7'b0100000]);
    wire inst_sllw      = (opcodeD[7'b0111011] & funct3D[3'b001] & funct7D[7'b0000000]);
    wire inst_srlw      = (opcodeD[7'b0111011] & funct3D[3'b101] & funct7D[7'b0000000]);
    wire inst_sraw      = (opcodeD[7'b0111011] & funct3D[3'b101] & funct7D[7'b0100000]);
    // RV64M
    wire inst_mulw      = (opcodeD[7'b0111011] & funct3D[3'b000] & funct7D[7'b0000001]);
    wire inst_divw      = (opcodeD[7'b0111011] & funct3D[3'b100] & funct7D[7'b0000001]);
    wire inst_divuw     = (opcodeD[7'b0111011] & funct3D[3'b101] & funct7D[7'b0000001]);
    wire inst_remw      = (opcodeD[7'b0111011] & funct3D[3'b110] & funct7D[7'b0000001]);
    wire inst_remuw     = (opcodeD[7'b0111011] & funct3D[3'b111] & funct7D[7'b0000001]);
    // Other
    wire inst_mret      = (opcodeD[7'b1110011] & funct3D[3'b000] & funct7D[7'b0011000] & rd==0 & rs1==0);
    wire inst_valid     = R_type | I_type | S_type | U_type | B_type | J_type;
/* ************************* Decode ************************* */


/* ************************* Type ************************* */
    // R-type
    wire R_type_arith = inst_add | inst_addw | inst_sub   | inst_subw
                      | inst_and | inst_xor  | inst_or
                      | inst_slt | inst_sltu | inst_sll   | inst_srl   | inst_sra   | inst_sllw | inst_srlw | inst_sraw
                      | inst_mul | inst_mulh | inst_mulhsu| inst_mulhu | inst_mulw
                      | inst_div | inst_divu | inst_divw  | inst_divuw
                      | inst_rem | inst_remu | inst_remw  | inst_remuw ;
    wire R_type_other = inst_mret;
    assign R_type     = R_type_arith | R_type_other;
    // I-type
    wire I_type_arith = inst_addi  | inst_addiw
                      | inst_andi  | inst_ori   | inst_xori
                      | inst_slti  | inst_sltiu
                      | inst_slli  | inst_srli  | inst_srai  | inst_slliw | inst_srliw | inst_sraiw ;
    wire I_type_load  = inst_ld    | inst_lw    | inst_lh    | inst_lb    | inst_lwu   | inst_lhu   | inst_lbu ;
    wire I_type_jump  = inst_jalr;
    wire I_type_csrr  = inst_csrrc | inst_csrrs | inst_csrrw ;
    wire I_type_csri  = inst_csrrci| inst_csrrsi| inst_csrrwi;
    wire I_type_other = inst_ecall | inst_ebreak| inst_fence_i;
    assign I_type     = I_type_arith | I_type_load | I_type_jump | I_type_csri | I_type_csrr | I_type_other;
    // S-type
    assign S_type     = inst_sd     | inst_sw    | inst_sh    | inst_sb;
    // U-type
    assign U_type     = inst_lui    | inst_auipc;
    // B-type
    assign B_type     = inst_beq    | inst_bne   | inst_blt   | inst_bge   | inst_bltu  | inst_bgeu;
    // J-type
    assign J_type     = inst_jal;
/* ************************* Type ************************* */


/* ************************* Immediate ************************* */
    wire [11:0] imm_I = inst[31:20];
    wire [11:0] imm_S = { inst[31:25], inst[11:7] };
    wire [11:0] imm_B = { inst[31], inst[7], inst[30:25], inst[11:8] };
    wire [19:0] imm_U = inst[31:12];
    wire [19:0] imm_J = { inst[31], inst[19:12], inst[20], inst[30:21] };

    wire [63:0] imm = ({64{I_type}} & { {52{imm_I[11]}}, imm_I       })
                    | ({64{S_type}} & { {52{imm_S[11]}}, imm_S       })
                    | ({64{U_type}} & { {32{imm_U[19]}}, imm_U, 12'b0})
                    | ({64{B_type}} & { {51{imm_B[11]}}, imm_B, 1'b0 })
                    | ({64{J_type}} & { {43{imm_J[19]}}, imm_J, 1'b0 }) ;
/* ************************* Immediate ************************* */


/* ************************* Pipeline ************************* */
    wire id_ready = (~rf_blk) & (~csr_blk) & (~bj_stop);

/* -------------- StageIF <=> StageID -------------- */
    assign {
        // to StageID
        id_pc,
        id_inst,
        id_pc_plus_4,
        if2id_excp_flags
    } = if2id_bus_R;

    assign  id2if_allow = (!id_valid) | (id_ready & ex2id_allow);
    assign  id2ex_valid = id_valid & id_ready;
    assign  inst        = id_inst;

    always @ (posedge clock) begin
        if (reset | csr_taken) begin
            id_valid <= 1'b0;
        end
        else if ((bj_taken) & if2id_fired & id2if_allow) begin
            id_valid <= 1'b0;
        end
        else if (id_fencei_taken) begin
            id_valid <= 1'b0;
        end
        else if (id2if_allow) begin
            id_valid <= if2id_fired;
        end
    end

    always @ (posedge clock) begin
        if (reset) begin
            if2id_bus_R <= 0;
        end
        else if (if2id_fired & id2if_allow) begin
            if2id_bus_R <= if2id_bus;
        end
    end
/* -------------- StageIF <=> StageID -------------- */


/* -------------- StageID <=> StageEX -------------- */
    assign id2ex_bus = {
        // to StageEX
        id_pc,
        id_inst,
        id_next_pc,
        id_excp_flags,
        id_intr_flags,
        alu_op,
        alu_word,
        alu_unsign,
        alu_mul_signs,
        alu_mul_high,
        alu_src1,
        alu_src2,
        mem_en,
        mem_we,
        mem_op,
        mem_size,
        mem_wdata,
        inst_fence_i,
        // to StageMA
        load_sext,
        rf_sel,
        csr_rdata,
        // to Regfile
        rf_we,
        rf_waddr,
        // to CSR
        inst_mret,
        csr_we,
        csr_waddr,
        csr_wdata
    };
/* -------------- StageID <=> StageEX -------------- */
/* ************************* Pipeline ************************* */


/* ************************* CSR ************************* */
    wire [63:0] csr_zimm   = { 59'h0, inst[19:15] };
    wire [11:0] csr_addr   = imm_I;

    assign csr_we   = inst_csrrc | inst_csrrs | inst_csrrw
                    | inst_csrrci| inst_csrrsi| inst_csrrwi;
    assign csr_waddr = csr_addr;
    assign csr_wdata = ({64{inst_csrrc }} & (~real_rs1 & csr_rdata))
                     | ({64{inst_csrrs }} & ( real_rs1 | csr_rdata))
                     | ({64{inst_csrrw }} & ( real_rs1            ))
                     | ({64{inst_csrrci}} & (~csr_zimm & csr_rdata))
                     | ({64{inst_csrrsi}} & ( csr_zimm | csr_rdata))
                     | ({64{inst_csrrwi}} & ( csr_zimm            ));

    assign csr_raddr = csr_addr;
/* ---------------------------- CSR Block ---------------------------- */
    localparam CSR_MSTATUS_ADDR =   12'h300;
    localparam CSR_MIE_ADDR     =   12'h304;

    // 避免出现前一条指令关闭中断, 下一条指令却产生中断的情况 // 是否有必要 ??
    wire csr_is_mstatus = (ex_csr_waddr == CSR_MSTATUS_ADDR)
                        | (ma_csr_waddr == CSR_MSTATUS_ADDR)
                        | (wb_csr_waddr == CSR_MSTATUS_ADDR);
    wire csr_is_mie     = (ex_csr_waddr == CSR_MIE_ADDR)
                        | (ma_csr_waddr == CSR_MIE_ADDR)
                        | (wb_csr_waddr == CSR_MIE_ADDR);

    assign { ex_csr_blk_flag, ex_csr_waddr} = ex_csr_blk_bus;
    assign { ma_csr_blk_flag, ma_csr_waddr} = ma_csr_blk_bus;
    assign { wb_csr_blk_flag, wb_csr_waddr} = wb_csr_blk_bus;

    assign ex_csr_blk = ex_csr_blk_flag & (csr_waddr == ex_csr_waddr | csr_is_mstatus | csr_is_mie);
    assign ma_csr_blk = ma_csr_blk_flag & (csr_waddr == ma_csr_waddr | csr_is_mstatus | csr_is_mie);
    assign wb_csr_blk = wb_csr_blk_flag & (csr_waddr == wb_csr_waddr | csr_is_mstatus | csr_is_mie);
    assign csr_blk = ex_csr_blk | ma_csr_blk | wb_csr_blk;
/* ---------------------------- CSR Block ---------------------------- */
/* ************************* CSR ************************* */


/* **************************************** RegFile **************************************** */
    wire [4:0]  rf_raddr1 = rs1;
    wire [4:0]  rf_raddr2 = rs2;
    assign  id2rf_bus = { rf_raddr1, rf_raddr2 };

    assign  { rf_rdata1, rf_rdata2 } = rf2id_bus;

    assign rf_we    = U_type       | J_type
                    | I_type_arith | I_type_load | I_type_jump
                    | I_type_csrr  | I_type_csri | R_type_arith;
    assign rf_waddr = rd;

    wire read_rs1 = S_type       | B_type      | R_type_arith
                  | I_type_arith | I_type_load | I_type_jump | I_type_csrr;
    wire read_rs2 = S_type | B_type | R_type_arith ;

    wire ex_rf_blk_flag, ma_rf_blk_flag, wb_rf_blk_flag;
    wire ex_rf_fwd_flag, ma_rf_fwd_flag, wb_rf_fwd_flag;
    wire [4:0] ex_rf_waddr, ma_rf_waddr, wb_rf_waddr;
    wire [63:0] ex_rf_fwd_data, ma_rf_fwd_data, wb_rf_fwd_data;

    wire rf_sel_mem = I_type_load;
    wire rf_sel_csr = I_type_csri | I_type_csrr;

    assign rf_sel[0] = ~ (rf_sel_mem | rf_sel_csr); // ALU
    assign rf_sel[1] = rf_sel_mem;                  // MEM
    assign rf_sel[2] = rf_sel_csr;                  // CSR

/* ---------------------------- RegFile Forward & Block ---------------------------- */
    assign { ex_rf_blk_flag, ex_rf_fwd_flag, ex_rf_waddr, ex_rf_fwd_data } = ex_rf_hzd_bus;
    assign { ma_rf_blk_flag, ma_rf_fwd_flag, ma_rf_waddr, ma_rf_fwd_data } = ma_rf_hzd_bus;
    assign { wb_rf_blk_flag, wb_rf_fwd_flag, wb_rf_waddr, wb_rf_fwd_data } = wb_rf_hzd_bus;

    wire id_ex_rs1_eq = (rf_raddr1 == ex_rf_waddr);
    wire id_ex_rs2_eq = (rf_raddr2 == ex_rf_waddr);
    wire id_ma_rs1_eq = (rf_raddr1 == ma_rf_waddr);
    wire id_ma_rs2_eq = (rf_raddr2 == ma_rf_waddr);
    wire id_wb_rs1_eq = (rf_raddr1 == wb_rf_waddr);
    wire id_wb_rs2_eq = (rf_raddr2 == wb_rf_waddr);

    // Regfile Block
    assign ex_rf_blk = ex_rf_blk_flag & (
                        read_rs1 & id_ex_rs1_eq |
                        read_rs2 & id_ex_rs2_eq
                    );
    assign ma_rf_blk = ma_rf_blk_flag & (
                        read_rs1 & id_ma_rs1_eq |
                        read_rs2 & id_ma_rs2_eq
                    );
    assign wb_rf_blk = wb_rf_blk_flag & (
                        read_rs1 & id_wb_rs1_eq |
                        read_rs2 & id_wb_rs2_eq
                    );
    assign rf_blk = ex_rf_blk | ma_rf_blk | wb_rf_blk;

    // Regfile Forward
    assign ex_rs1_fwd = ex_rf_fwd_flag & id_ex_rs1_eq;
    assign ex_rs2_fwd = ex_rf_fwd_flag & id_ex_rs2_eq;
    assign ma_rs1_fwd = ma_rf_fwd_flag & id_ma_rs1_eq;
    assign ma_rs2_fwd = ma_rf_fwd_flag & id_ma_rs2_eq;
    assign wb_rs1_fwd = wb_rf_fwd_flag & id_wb_rs1_eq;
    assign wb_rs2_fwd = wb_rf_fwd_flag & id_wb_rs2_eq;

    assign real_rs1 = (ex_rs1_fwd & read_rs1) ? ex_rf_fwd_data
                    : (ma_rs1_fwd & read_rs1) ? ma_rf_fwd_data
                    : (wb_rs1_fwd & read_rs1) ? wb_rf_fwd_data
                    : rf_rdata1;
    assign real_rs2 = (ex_rs2_fwd & read_rs2) ? ex_rf_fwd_data
                    : (ma_rs2_fwd & read_rs2) ? ma_rf_fwd_data
                    : (wb_rs2_fwd & read_rs2) ? wb_rf_fwd_data
                    : rf_rdata2;
/* ---------------------------- RegFile Forward & Block ---------------------------- */
/* **************************************** RegFile **************************************** */


/* ************************* ALU ************************* */
    wire alu_src1_is_pc  = inst_jal | inst_jalr | inst_auipc
                         | inst_beq | inst_bne  | inst_blt
                         | inst_bge | inst_bltu | inst_bgeu;
    wire alu_src1_is_0   = inst_lui ;
    wire alu_src1_is_rs1 = ~(alu_src1_is_pc | alu_src1_is_0);
    wire alu_src2_is_imm = S_type   | U_type | I_type_arith | I_type_load;
    wire alu_src2_is_4   = inst_jal | inst_jalr  ;
    wire alu_src2_is_rs2 = ~(alu_src2_is_imm | alu_src2_is_4);

    assign alu_op[ 0] = inst_add   | inst_addi  | inst_addw  | inst_addiw | inst_auipc | inst_lui
                      | inst_ld    | inst_lw    | inst_lh    | inst_lb    | inst_lwu   | inst_lhu   | inst_lbu
                      | inst_sd    | inst_sw    | inst_sh    | inst_sb
                      | inst_beq   | inst_bne   | inst_blt   | inst_bge   | inst_bltu  | inst_bgeu
                      | inst_jal   | inst_jalr;
    assign alu_op[ 1] = inst_sub   | inst_subw;
    assign alu_op[ 2] = inst_slt   | inst_slti  | inst_sltu  | inst_sltiu;
    assign alu_op[ 3] = inst_and   | inst_andi;
    assign alu_op[ 4] = inst_or    | inst_ori;
    assign alu_op[ 5] = inst_xor   | inst_xori;
    assign alu_op[ 6] = inst_sll   | inst_slli  | inst_sllw  |inst_slliw ;
    assign alu_op[ 7] = inst_srl   | inst_srli  | inst_srlw  |inst_srliw;
    assign alu_op[ 8] = inst_sra   | inst_srai  | inst_sraw  |inst_sraiw;
    assign alu_op[ 9] = inst_mul   | inst_mulh  | inst_mulhsu| inst_mulhu | inst_mulw ;
    assign alu_op[10] = inst_div   | inst_divu  | inst_divw  | inst_divuw ;
    assign alu_op[11] = inst_rem   | inst_remu  | inst_remw  | inst_remuw ;

    assign alu_word = inst_addw  | inst_addiw | inst_subw
                    | inst_sllw  | inst_srlw  | inst_sraw
                    | inst_slliw | inst_srliw | inst_sraiw
                    | inst_mulw  | inst_divw  | inst_divuw | inst_remw  | inst_remuw ;
    assign alu_unsign = inst_sltu  | inst_sltiu
                      | inst_divu  | inst_divuw
                      | inst_remu  | inst_remuw;
    assign alu_mul_signs = {2{(inst_mul | inst_mulh | inst_mulw)}} & 2'b11
                         | {2{(inst_mulhsu)}}                      & 2'b10
                         | {2{(inst_mulhu)}}                       & 2'b00 ;
    assign alu_mul_high = inst_mulh | inst_mulhsu | inst_mulhu;

    assign alu_src1 = {64{alu_src1_is_rs1}} & real_rs1
                    | {64{alu_src1_is_pc }} & id_pc
                    | {64{alu_src1_is_0  }} & 64'h0;
    assign alu_src2 = {64{alu_src2_is_rs2}} & real_rs2
                    | {64{alu_src2_is_imm}} & imm
                    | {64{alu_src2_is_4  }} & 64'h4;
/* ************************* ALU ************************* */


/* ************************* MEM ************************* */
    assign mem_op[0] = inst_sb  | inst_lb   | inst_lbu ;
    assign mem_op[1] = inst_sh  | inst_lh   | inst_lhu ;
    assign mem_op[2] = inst_sw  | inst_lw   | inst_lwu ;
    assign mem_op[3] = inst_sd  | inst_ld              ;

    assign mem_en   = |mem_op;
    assign mem_we   = inst_sd  | inst_sw   | inst_sh  | inst_sb;
    assign mem_size = ({3{mem_op[3]}}) & 3'b011
                    | ({3{mem_op[2]}}) & 3'b010
                    | ({3{mem_op[1]}}) & 3'b001
                    | ({3{mem_op[0]}}) & 3'b000 ;
    assign load_sext =    inst_lw   | inst_lh   | inst_lb ;
    assign mem_wdata = real_rs2;
/* ************************* MEM ************************* */


/* ************************************* Trap ************************************* */
    assign id_excp_flags[1:0]   = if2id_excp_flags[1:0];
    assign id_excp_flags[2]     = id_valid & (~|if2id_excp_flags[1:0])  & ~inst_valid;
    assign id_excp_flags[3]     = id_valid & (~|if2id_excp_flags[2:0])  &  inst_ebreak;
    assign id_excp_flags[10:4]  = 7'b0;
    assign id_excp_flags[11]    = id_valid & (~|if2id_excp_flags[10:0]) & inst_ecall;
    assign id_excp_flags[15:12] = 4'b0;

    assign id_intr_flags[2:0] = 3'b0;
    assign id_intr_flags[3] = csr_intr[0] & (~csr_blk); // 避免错误地标记中断
    assign id_intr_flags[6:4] = 3'b0;
    assign id_intr_flags[7] = csr_intr[1] & (~csr_blk); // 避免错误地标记中断
    assign id_intr_flags[15:8] = 8'b0;

/* ----------------------------- Cancel Flag ----------------------------- */
    assign id_cancel = id_valid & ((|id_excp_flags) | (|id_intr_flags) | inst_mret | inst_fence_i);
/* ----------------------------- Cancel Flag ----------------------------- */

/* ************************************* Trap ************************************* */


/* ************************* Branch & Jump ************************* */
    assign bj_info = {
        inst_jal, inst_jalr, inst_bgeu, inst_bltu,
        inst_bge, inst_blt, inst_bne, inst_beq
    };

    assign bj_stop = (ex_rs1_fwd | ex_rs2_fwd | ma_rs1_fwd | ma_rs2_fwd) & (|bj_info[6:0]);

    wire [63:0] bj_src1 = wb_rs1_fwd ? wb_rf_fwd_data
                        : rf_rdata1;
    wire [63:0] bj_src2 = wb_rs2_fwd ? wb_rf_fwd_data
                        : rf_rdata2;

    ysyx_22050293_BranchJumpUnit bju (
        .pc         (id_pc),
        .rs1        (bj_src1),
        .rs2        (bj_src2),
        .imm        (imm),
        .bj_info    (bj_info),
        .bj_flag    (bj_flag),
        .bj_target  (bj_target)
    );

    assign bj_taken = bj_flag & id_valid &  id_ready & (~bj_stop);
    assign bj_stall = bj_flag & id_valid & ~id_ready & (~bj_stop);

    assign bj_bus = { bj_taken, bj_stall, bj_target };

    assign id_next_pc = bj_taken ? bj_target : id_pc_plus_4;
/* ************************* Branch & Jump ************************* */

    // fence.i
    assign id_fencei_taken = inst_fence_i & id_valid & id_ready & ex2id_allow;
    assign id_fencei_target = id_pc_plus_4;

endmodule


module ysyx_22050293_StageEX (
    input           clock,
    input           reset,

    // StageEX -> StageMA
    output          ex2ma_valid,
    output [445:0]  ex2ma_bus,

    // StageMA -> StageEX
    input           ma2ex_allow,

    // StageEX -> StageID
    output          ex2id_allow,

    // StageID -> StageEX
    input           id2ex_valid,
    input   [562:0] id2ex_bus,

    // CSR -> StageEX
    input           csr_taken,

    // StageEX -> StageID @ Forward & Block
    output  [70:0]  ex_rf_hzd_bus,
    output  [12:0]  ex_csr_blk_bus,

    // StageMA -> StageEX @ Cancel Flag
    input           ma_cancel,

    // StageWB -> StageEX @ Cancel Flag
    input           wb_cancel,
    // StageEX -> StageIF @ Cancel Flag
    output          ex_cancel,

    /* ********* DataBus Interface ********* */
    output          dbus_req,
    output          dbus_op,
    output  [2:0]   dbus_size,
    output  [7:0]   dbus_mask,
    output  [31:0]  dbus_addr,
    output  [63:0]  dbus_wdata,
    input           dbus_addr_ok,
    output          dbus_uncache,
    output          dbus_flush,
    output          dbus_clear,
    input           dbus_idle
    /* ********* DataBus Interface ********* */
);

/* ************************* Pipeline ************************* */

    reg     ex_valid;
    wire    ex_ready;

/* -------------- StageID <=> StageEX -------------- */
    // to StageEX signal
    wire [63:0]     ex_pc;
    wire [31:0]     ex_inst;
    wire [63:0]     ex_next_pc;
    /* lint_off UNUSEDSIGNAL */
    wire [15:0]     id2ex_excp_flags;
    wire [15:0]     id2ex_intr_flags;
    wire [11:0]     alu_op;
    wire            alu_word;
    wire            alu_unsign;
    wire [1:0]      alu_mul_signs;
    wire            alu_mul_high;
    wire [63:0]     alu_src1;
    wire [63:0]     alu_src2;
    // to StageMA signal
    wire            mem_en;
    wire            mem_we;
    wire [3:0]      mem_op;
    wire [31:0]     mem_addr;
    wire [2:0]      mem_size;
    wire [63:0]     mem_wdata;
    wire            fencei;
    wire            load_sext;
    wire [2:0]      rf_sel;
    wire [63:0]     csr_rdata;
    // to Regfile signal
    wire            ex_rf_we;
    wire [4:0]      ex_rf_waddr;
    // to CSR signal
    wire            inst_mret;
    wire            ex_csr_we;
    wire [11:0]     ex_csr_waddr;
    wire [63:0]     ex_csr_wdata;

    reg [562:0]     id2ex_bus_R;

    wire            alu_done;
    wire [63:0]     alu_result;

    wire            need_req;
    wire            need_flush;

    wire [15:0] ex_excp_flags;
    wire [15:0] ex_intr_flags;

    wire sb_op  = mem_op[0];
    wire sh_op  = mem_op[1];
    wire sw_op  = mem_op[2];
    wire sd_op  = mem_op[3];

    assign {
        // to StageEX
        ex_pc,
        ex_inst,
        ex_next_pc,
        id2ex_excp_flags,
        id2ex_intr_flags,
        alu_op,
        alu_word,
        alu_unsign,
        alu_mul_signs,
        alu_mul_high,
        alu_src1,
        alu_src2,
        mem_en,
        mem_we,
        mem_op,
        mem_size,
        mem_wdata,
        fencei,
        // to StageMA
        load_sext,
        rf_sel,
        csr_rdata,
        // to Regfile
        ex_rf_we,
        ex_rf_waddr,
        // to CSR
        inst_mret,
        ex_csr_we,
        ex_csr_waddr,
        ex_csr_wdata
    } = id2ex_bus_R;

    assign ex_ready = alu_done & (~need_req & ~need_flush | dbus_req & dbus_addr_ok | dbus_flush & dbus_idle);

    assign  ex2id_allow = (~ex_valid) | (ex_ready & ma2ex_allow);
    assign  ex2ma_valid = ex_valid & ex_ready;

    always @ (posedge clock) begin
        if (reset | csr_taken) begin
            ex_valid <= 1'b0;
        end
        else if (ex2id_allow) begin
            ex_valid <= id2ex_valid;
        end
    end

    always @ (posedge clock) begin
        if (reset) begin
            id2ex_bus_R <= 0;
        end
        else if (id2ex_valid & ex2id_allow) begin
            id2ex_bus_R <= id2ex_bus;
        end
    end
/* -------------- StageID <=> StageEX -------------- */

/* -------------- StageEX <=> StageMA -------------- */
    wire mem_has_req    = dbus_req;
    wire has_flush      = dbus_flush;

    assign ex2ma_bus = {
        // to StageMA
        ex_pc,
        ex_inst,
        ex_next_pc,
        ex_excp_flags,
        ex_intr_flags,
        mem_op,
        mem_addr,
        load_sext,
        mem_has_req,
        rf_sel,
        alu_result,
        csr_rdata,
        has_flush,
        // to Regfile
        ex_rf_we,
        ex_rf_waddr,
        // to CSR
        inst_mret,
        ex_csr_we,
        ex_csr_waddr,
        ex_csr_wdata
    };
    /* -------------- StageEX <=> StageMA -------------- */
/* ************************* Pipeline ************************* */


/* ************************* ALU ************************* */
    ysyx_22050293_ALU alu (
        .clock      (clock),
        .reset      (reset),
        .flush      (csr_taken),
        .valid      (ex_valid),
        .operation  (alu_op),
        .src1       (alu_src1),
        .src2       (alu_src2),
        .word       (alu_word),
        .unsign     (alu_unsign),
        .mul_signs  (alu_mul_signs),
        .mul_high   (alu_mul_high),
        .result     (alu_result),
        .done       (alu_done)
    );
/* ************************* ALU ************************* */


/* ******************************** RegFile ******************************** */
    // RegFile Forward & Block
    wire        is_read_mem = (mem_en & ~mem_we);
    wire        ex_rf_blk_flag = ex_valid & ex_rf_we & (|ex_rf_waddr) &  (is_read_mem | ~ex_ready);
    wire        ex_rf_fwd_flag = ex_valid & ex_rf_we & (|ex_rf_waddr) & ~(is_read_mem | ~ex_ready);
    wire [63:0] ex_rf_fwd_data = alu_result;
    assign ex_rf_hzd_bus = {
        ex_rf_blk_flag,
        ex_rf_fwd_flag,
        ex_rf_waddr,
        ex_rf_fwd_data
    };
/* ******************************** RegFile ******************************** */


/* ************************* CSR ************************* */
    // CSR Block
    wire ex_csr_blk_flag = ex_valid & ex_csr_we;
    assign ex_csr_blk_bus = {
        ex_csr_blk_flag,
        ex_csr_waddr
    };
/* ************************* CSR ************************* */


/* ************************************* Trap ************************************* */
    wire has_other_excp = (|id2ex_excp_flags[3:0]) | (id2ex_excp_flags[5]) | (|id2ex_excp_flags[15:7]);
    wire addr_misaligned = (sh_op & (mem_addr[0:0] != 0))
                         | (sw_op & (mem_addr[1:0] != 0))
                         | (sd_op & (mem_addr[2:0] != 0));

    assign ex_excp_flags[3:0]= id2ex_excp_flags[3:0];
    assign ex_excp_flags[4] = ex_valid & (~has_other_excp) & mem_en & (~mem_we) & addr_misaligned;
    assign ex_excp_flags[5] = id2ex_excp_flags[5];
    assign ex_excp_flags[6] = ex_valid & (~has_other_excp) & mem_en & ( mem_we) & addr_misaligned;
    assign ex_excp_flags[15:7]= id2ex_excp_flags[15:7];
    assign ex_intr_flags = id2ex_intr_flags;

/* ----------------------------- Cancel Flag ----------------------------- */
    wire ex_has_intr    = ex_valid & (|ex_intr_flags);
    wire ex_has_excp    = ex_valid & (|ex_excp_flags);
    wire ex_has_mret    = ex_valid & inst_mret;
    wire ex_has_fencei  = ex_valid & fencei;

    wire ex_has_trap = ex_has_intr | ex_has_excp | ex_has_mret ;

    assign ex_cancel = ex_has_intr | ex_has_excp | ex_has_mret | ex_has_fencei;

    wire cancel_memory = (ex_has_excp | ex_has_mret) // 不要加入 ex intr !!!!!!!
                       | ma_cancel
                       | wb_cancel;
/* ----------------------------- Cancel Flag ----------------------------- */
/* ************************************* Trap ************************************* */


/* ************************* MEM ************************* */
    assign mem_addr = alu_result[31:0];

    localparam [7:0] LUT_SB [8] = '{
        8'b00000001, 8'b00000010, 8'b00000100, 8'b00001000,
        8'b00010000, 8'b00100000, 8'b01000000, 8'b10000000
    };
    localparam [7:0] LUT_SH [4] = '{
        8'b00000011, 8'b00001100, 8'b00110000, 8'b11000000
    };
    localparam [7:0] LUT_SW [2] = '{
        8'b00001111, 8'b11110000
    };
    localparam [7:0] LUT_SD = 8'b11111111;

    wire [7:0] mem_mask = {8{sb_op}} & LUT_SB[mem_addr[2:0]]
                        | {8{sh_op}} & LUT_SH[mem_addr[2:1]]
                        | {8{sw_op}} & LUT_SW[mem_addr[2:2]]
                        | {8{sd_op}} & LUT_SD;

    wire [63:0] store_data = {64{sb_op}} & ({8{mem_wdata[ 7:0]}})
                           | {64{sh_op}} & ({4{mem_wdata[15:0]}})
                           | {64{sw_op}} & ({2{mem_wdata[31:0]}})
                           | {64{sd_op}} & (mem_wdata) ;

    assign need_req     = mem_en & ex_valid & (~cancel_memory);
    assign need_flush   = fencei & ex_valid & (~ex_has_trap & ~ma_cancel & ~wb_cancel);

    assign dbus_req     = need_req & ma2ex_allow;
    assign dbus_op      = mem_we;
    assign dbus_size    = mem_size;
    assign dbus_mask    = mem_mask;
    assign dbus_addr    = mem_addr;
    assign dbus_wdata   = store_data;
    assign dbus_uncache = ~(dbus_addr >= 32'h8000_0000 && dbus_addr < 32'hFC00_0000);

    assign dbus_flush   = need_flush & ma2ex_allow;
    assign dbus_clear   = 0;

/* ************************* MEM ************************* */

endmodule


module ysyx_22050293_StageMA (
    input           clock,
    input           reset,

    // StageMA -> StageEX
    output          ma2ex_allow,

    // StageEX -> StageMA
    input           ex2ma_valid,
    input   [445:0] ex2ma_bus,

    // StageMA -> StageWB
    output          ma2wb_valid,
    output  [339:0] ma2wb_bus,

    // StageWB -> StageMA
    input           wb2ma_allow,

    // CSR -> StageEX
    input           csr_taken,

    // StageMA -> StageID @ Forward & Block
    output   [70:0] ma_rf_hzd_bus,
    output   [12:0] ma_csr_blk_bus,

    // StageMA -> StageIF/EX @ Cancel Flag
    output          ma_cancel,

    /* ********* DataBus Interface ********* */
    input   [63:0]  dbus_rdata,
    input           dbus_data_ok,
    input           dbus_load_fault,
    input           dbus_store_fault,
    input           dbus_done
    /* ********* DataBus Interface ********* */
);

/* ************************* Pipeline ************************* */
    reg     ma_valid;
    wire    ma_ready;
/* -------------- StageEX <=> StageMA -------------- */
    // to StageMA signal
    wire [63:0]     ma_pc;
    wire [31:0]     ma_inst;
    wire [63:0]     ma_next_pc;
    /* lint_off UNUSEDSIGNAL */
    wire [15:0]     ex2ma_excp_flags;
    wire [15:0]     ex2ma_intr_flags;
    wire [3:0]      mem_op;
    wire [31:0]     mem_addr;
    wire            load_sext;
    wire            mem_has_req;
    wire [2:0]      rf_sel;
    wire [63:0]     alu_result;
    wire [63:0]     csr_rdata;
    wire            has_flush;
    // to Regfile signal
    wire            ma_rf_we;
    wire [4:0]      ma_rf_waddr;
    // to CSR signal
    wire            inst_mret;
    wire            ma_csr_we;
    wire [11:0]     ma_csr_waddr;
    wire [63:0]     ma_csr_wdata;

    reg  [445:0]    ex2ma_bus_R;

    assign {
        // to StageMA
        ma_pc,
        ma_inst,
        ma_next_pc,
        ex2ma_excp_flags,
        ex2ma_intr_flags,
        mem_op,
        mem_addr,
        load_sext,
        mem_has_req,
        rf_sel,
        alu_result,
        csr_rdata,
        has_flush,
        // to Regfile
        ma_rf_we,
        ma_rf_waddr,
        // to CSR
        inst_mret,
        ma_csr_we,
        ma_csr_waddr,
        ma_csr_wdata
    } = ex2ma_bus_R;

    assign  ma_ready    = (~mem_has_req & ~has_flush) | (mem_has_req & dbus_data_ok) | (has_flush & dbus_done);
    assign  ma2ex_allow = (~ma_valid) | (ma_ready & wb2ma_allow);
    assign  ma2wb_valid = ma_valid & ma_ready;

    always @ (posedge clock) begin
        if (reset | csr_taken) begin
            ma_valid <= 1'b0;
        end
        else if (ma2ex_allow) begin
            ma_valid <= ex2ma_valid;
        end
    end

    always @ (posedge clock) begin
        if (reset) begin
            ex2ma_bus_R <= 0;
        end
        else if (ex2ma_valid & ma2ex_allow) begin
            ex2ma_bus_R <= ex2ma_bus;
        end
    end
/* -------------- StageEX <=> StageMA -------------- */

/* -------------- StageMA <=> StageWB -------------- */
    wire [15:0]     ma_excp_flags;
    wire [15:0]     ma_intr_flags;

    wire [63:0]     ma_rf_wdata;

    assign ma2wb_bus = {
        // to StageWB
        ma_pc,
        ma_inst,
        ma_next_pc,
        ma_excp_flags,
        ma_intr_flags,
        // to Regfile
        ma_rf_we,
        ma_rf_waddr,
        ma_rf_wdata,
        // to CSR
        inst_mret,
        ma_csr_we,
        ma_csr_waddr,
        ma_csr_wdata
    };
/* -------------- StageMA <=> StageWB -------------- */
/* ************************* Pipeline ************************* */

/* ************************* MEM ************************* */
    wire lb_op  = mem_op[0];
    wire lh_op  = mem_op[1];
    wire lw_op  = mem_op[2];
    wire ld_op  = mem_op[3];

    wire [63:0] rdata   = dbus_rdata;
    wire [7:0]  lb_data = rdata[{mem_addr[2:0], 3'b0} +: 8 ];
    wire [15:0] lh_data = rdata[{mem_addr[2:1], 4'b0} +: 16];
    wire [31:0] lw_data = rdata[{mem_addr[2]  , 5'b0} +: 32];
    wire [63:0] ld_data = rdata;

    wire [63:0] load_data = {64{lb_op}} & { {56{load_sext & lb_data[ 7]}}, lb_data }
                          | {64{lh_op}} & { {48{load_sext & lh_data[15]}}, lh_data }
                          | {64{lw_op}} & { {32{load_sext & lw_data[31]}}, lw_data }
                          | {64{ld_op}} &                                  ld_data ;
/* ************************* MEM ************************* */


/* ************************* CSR ************************* */
    // CSR Block
    wire ma_csr_blk_flag = ma_valid & ma_csr_we;
    assign ma_csr_blk_bus = {
        ma_csr_blk_flag,
        ma_csr_waddr
    };
/* ************************* CSR ************************* */


/* ******************************** RegFile ******************************** */

    // RegFile Write Data
    assign ma_rf_wdata = {64{rf_sel[0]}} & alu_result
                            | {64{rf_sel[1]}} & load_data
                            | {64{rf_sel[2]}} & csr_rdata  ;
    // RegFile Block & Forward
    wire        ma_rf_blk_flag  = (ma_valid & ~ma_ready) & ma_rf_we & (|ma_rf_waddr);
    wire        ma_rf_fwd_flag  = (ma_valid &  ma_ready) & ma_rf_we & (|ma_rf_waddr);
    wire [63:0] ma_rf_fwd_data  = ma_rf_wdata;

    assign ma_rf_hzd_bus = {
        ma_rf_blk_flag,
        ma_rf_fwd_flag,
        ma_rf_waddr,
        ma_rf_fwd_data
    };
/* ******************************** RegFile ******************************** */


/* ************************************* Trap ************************************* */
    wire has_other_excp = (|ex2ma_excp_flags[4:0])
                        | ( ex2ma_excp_flags[6])
                        | (|ex2ma_excp_flags[15:8]);

    assign ma_excp_flags[4:0] = ex2ma_excp_flags[4:0];
    assign ma_excp_flags[5]   = ma_valid & (~has_other_excp) & dbus_load_fault;
    assign ma_excp_flags[6]   = ex2ma_excp_flags[6];
    assign ma_excp_flags[7]   = ma_valid & (~has_other_excp) & dbus_store_fault;
    assign ma_excp_flags[15:8]= ex2ma_excp_flags[15:8];

    assign ma_intr_flags = ex2ma_intr_flags;

/* ----------------------------- Cancel Flag ----------------------------- */
    assign ma_cancel = ma_valid & ((|ma_intr_flags) | (|ma_excp_flags) | inst_mret | (has_flush & ~dbus_done));
/* ----------------------------- Cancel Flag ----------------------------- */

/* ************************************* Trap ************************************* */

endmodule


module ysyx_22050293_StageWB (
    input           clock,
    input           reset,

    // StageWB -> Regfile
    output  [69:0]  wb2rf_bus,

    // StageWB -> CSR
    output  [206:0] wb2csr_bus,

    // CSR -> StageWB
    input           csr_taken,

    // StageWB <- StageMA
    input           ma2wb_valid,
    input   [339:0] ma2wb_bus,

    // StageWB -> StageMA
    output          wb2ma_allow,

    // StageWB -> StageIF/EX @ Cancel Flag
    output          wb_cancel,

    // Forward & Block from StageWB to StageID
    output  [70:0]  wb_rf_hzd_bus,
    output  [12:0]  wb_csr_blk_bus
);

/* ************************* Pipeline ************************* */
    reg     wb_valid;
    wire    wb_ready = 1'b1;

/* -------------------- StageMA <=> StageWB -------------------- */
    // to StageWB signal
    wire [63:0]     wb_pc;
    wire [31:0]     wb_inst;
    wire [63:0]     wb_next_pc;
    wire [15:0]     ma2wb_excp_flags;
    wire [15:0]     ma2wb_intr_flags;
    // to Regfile signal
    wire            ma2wb_rf_we;
    wire [4:0]      wb_rf_waddr;
    wire [63:0]     wb_rf_wdata;
    // to CSR signal
    wire            inst_mret;
    wire            ma2wb_csr_we;
    wire [11:0]     wb_csr_waddr;
    wire [63:0]     wb_csr_wdata;

    wire            wb_csr_we;
    wire            wb_mret_taken;
    wire            wb_excp_taken;
    wire            wb_intr_taken;
    wire            wb_trap_taken;
    wire [63:0]     wb_trap_cause;


    reg [339:0]     ma2wb_bus_R;

    assign {
        // to StageWB
        wb_pc,
        wb_inst,
        wb_next_pc,
        ma2wb_excp_flags,
        ma2wb_intr_flags,
        // to Regfile
        ma2wb_rf_we,
        wb_rf_waddr,
        wb_rf_wdata,
        // to CSR
        inst_mret,
        ma2wb_csr_we,
        wb_csr_waddr,
        wb_csr_wdata
    } = ma2wb_bus_R;

    assign  wb2ma_allow = (~wb_valid) | (wb_ready);

    always @ (posedge clock) begin
        if (reset | csr_taken) begin
            wb_valid <= 1'b0;
        end
        else if (wb2ma_allow) begin
            wb_valid <= ma2wb_valid;
        end
    end

    always @ (posedge clock) begin
        if (reset) begin
            ma2wb_bus_R <= 0;
        end else
        if (ma2wb_valid & wb2ma_allow) begin
            ma2wb_bus_R <= ma2wb_bus;
        end
    end
/* -------------------- StageMA <=> StageWB -------------------- */
/* ************************* Pipeline ************************* */


/* ******************************** RegFile ******************************** */
    wire wb_rf_we = ma2wb_rf_we & wb_valid & (~wb_excp_taken);
    assign wb2rf_bus = { wb_rf_we, wb_rf_waddr, wb_rf_wdata };

    // RegFile Forward & Block
    wire           wb_rf_blk_flag, wb_rf_fwd_flag;
    wire [63:0]    wb_rf_fwd_data;
    assign wb_rf_blk_flag = 1'b0;
    assign wb_rf_fwd_flag = wb_valid & ma2wb_rf_we & (|wb_rf_waddr);
    assign wb_rf_fwd_data = wb_rf_wdata;
    assign wb_rf_hzd_bus = {
        wb_rf_blk_flag,
        wb_rf_fwd_flag,
        wb_rf_waddr,
        wb_rf_fwd_data
    };
/* ******************************** RegFile ******************************** */


/* ************************************* Trap ************************************* */
    wire wb_has_mret = wb_valid & inst_mret;
    wire wb_has_excp = wb_valid & (|ma2wb_excp_flags);
    wire wb_has_intr = wb_valid & (|ma2wb_intr_flags);

/* ----------------------------- Cancel Flag ----------------------------- */
    assign wb_cancel = wb_has_excp | wb_has_intr | wb_has_mret;
/* ----------------------------- Cancel Flag ----------------------------- */
/* ************************************* Trap ************************************* */


/* ************************* CSR ************************* */
    // CSR Block
    wire   wb_csr_blk_flag = wb_valid & wb_csr_we;
    assign wb_csr_blk_bus = {
        wb_csr_blk_flag,
        wb_csr_waddr
    };

    // to CSR
    assign wb_csr_we = ma2wb_csr_we & wb_valid & (~wb_excp_taken);
    assign wb_mret_taken = wb_has_mret;
    assign wb_excp_taken = wb_has_excp;
    assign wb_intr_taken = wb_has_intr;
    assign wb_trap_taken = wb_excp_taken | wb_intr_taken;

    ysyx_22050293_Encoder_16_4    cause_code_encoder (
        .in(wb_excp_taken ? ma2wb_excp_flags : ma2wb_intr_flags),
        .out(wb_trap_cause[3:0])
    );
    assign wb_trap_cause[62:4] = 59'b0;
    assign wb_trap_cause[63] = wb_intr_taken;

    wire [63:0] wb_trap_pc = wb_excp_taken ? wb_pc : wb_next_pc;

    assign wb2csr_bus = {
        wb_csr_we,
        wb_csr_waddr,
        wb_csr_wdata,
        wb_mret_taken,
        wb_trap_taken,
        wb_trap_cause,
        wb_trap_pc
    };
/* ************************* CSR ************************* */

endmodule


module ysyx_22050293_Core (
    input                clock,
    input                reset,

    // Interrupts
    input  [1:0]         clint_intr,

    // InstBus interface
    output               ibus_req,
    output               ibus_op,
    output [2:0]         ibus_size,
    output [7:0]         ibus_mask,
    output [31:0]        ibus_addr,
    output [63:0]        ibus_wdata,
    input  [63:0]        ibus_rdata,
    input                ibus_addr_ok,
    input                ibus_data_ok,
    output               ibus_uncache,
    input                ibus_load_fault,
    input                ibus_store_fault,
    output               ibus_flush,
    output               ibus_clear,
    input                ibus_done,
    input                ibus_idle,

    // DataBus interface
    output               dbus_req,
    output               dbus_op,
    output [2:0]         dbus_size,
    output [7:0]         dbus_mask,
    output [31:0]        dbus_addr,
    output [63:0]        dbus_wdata,
    input  [63:0]        dbus_rdata,
    input                dbus_addr_ok,
    input                dbus_data_ok,
    output               dbus_uncache,
    input                dbus_load_fault,
    input                dbus_store_fault,
    output               dbus_flush,
    output               dbus_clear,
    input                dbus_done,
    input                dbus_idle
);

/* ************************ StageIF <=> StageID ************************ */
    // StageID -> StageIF
    wire            id2if_allow;
    wire [65:0]     bj_bus;
    wire            id_fencei_taken;
    wire [63:0]     id_fencei_target;
    // StageIF -> StageID
    wire            if2id_fired;
    wire [175:0]    if2id_bus;

/* ************************ StageIF <=> StageID ************************ */


/* ************************ StageID <=> StageEX ************************ */
    // StageID -> StageEX
    wire            id2ex_valid;
    wire [562:0]    id2ex_bus;
    // StageEX -> StageID
    wire            ex2id_allow;
/* ************************ StageID <=> StageEX ************************ */


/* ************************ StageEX <=> StageMA ************************ */
    // StageEX -> StageMA
    wire            ex2ma_valid;
    wire [445:0]    ex2ma_bus;
    // StageMA -> StageEX
    wire            ma2ex_allow;
/* ************************ StageEX <=> StageMA ************************ */


/* ************************ StageMA <=> StageWB ************************ */
    // StageWB -> StageMA
    wire            wb2ma_allow;
    // StageMA -> StageWB
    wire            ma2wb_valid;
    wire [339:0]    ma2wb_bus;
/* ************************ StageMA <=> StageWB ************************ */


/* ************************ StageID <=> Regfile ************************ */
    wire [4:0]      id_rf_raddr1;
    wire [63:0]     id_rf_rdata1;
    wire [4:0]      id_rf_raddr2;
    wire [63:0]     id_rf_rdata2;

    // StageID -> Regfile
    wire [9:0]      id2rf_bus;
    assign  { id_rf_raddr1, id_rf_raddr2 } = id2rf_bus;

    // Regfile -> StageID
    wire [127:0]    rf2id_bus;
    assign  rf2id_bus = { id_rf_rdata1, id_rf_rdata2 };
/* ************************ StageID <=> Regfile ************************ */


/* ************************ StageWB <=> Regfile ************************ */
    wire [69:0]     wb2rf_bus;

    wire            wb_rf_we;
    wire [4:0]      wb_rf_waddr;
    wire [63:0]     wb_rf_wdata;

    assign  { wb_rf_we, wb_rf_waddr, wb_rf_wdata } = wb2rf_bus;
/* ************************ StageWB <=> Regfile ************************ */


/* ************************ Forward & Block ************************ */
    // StageEX -> StageID @ Forward & Block
    wire [70:0]     ex_rf_hzd_bus;
    wire [12:0]     ex_csr_blk_bus;

    // StageMA -> StageID @ Forward & Block
    wire [70:0]     ma_rf_hzd_bus;
    wire [12:0]     ma_csr_blk_bus;

    // StageWB -> StageID @ Forward & Block
    wire [70:0]     wb_rf_hzd_bus;
    wire [12:0]     wb_csr_blk_bus;
/* ************************ Forward & Block ************************ */


/* ************************ Cancel Flag ************************ */
    wire   id_cancel;
    wire   ex_cancel;
    wire   ma_cancel;
    wire   wb_cancel;
/* ************************ Cancel Flag ************************ */


/* -------------------------------- Regfile -------------------------------- */
    ysyx_22050293_Regfile regfile (
        .clock      (clock),
        .reset      (reset),

        .we         (wb_rf_we),
        .waddr      (wb_rf_waddr),
        .wdata      (wb_rf_wdata),

        .raddr1     (id_rf_raddr1),
        .rdata1     (id_rf_rdata1),
        .raddr2     (id_rf_raddr2),
        .rdata2     (id_rf_rdata2)
    );
/* -------------------------------- Regfile -------------------------------- */


/* -------------------------------- CSR -------------------------------- */
    wire [11:0]     csr_raddr;
    wire [63:0]     csr_rdata;
    wire [206:0]    wb2csr_bus;
    wire            csr_taken;
    wire [63:0]     csr_target;
    wire [1:0]      csr_intr;

    ysyx_22050293_CSR csr (
        .clock      (clock),
        .reset      (reset),

        .csr_rdata  (csr_rdata),
        .csr_raddr  (csr_raddr),

        .wb2csr_bus (wb2csr_bus),

        .csr_taken  (csr_taken),
        .csr_target (csr_target),

        .clint_intr (clint_intr),
        .csr_intr   (csr_intr)
    );
/* -------------------------------- CSR -------------------------------- */


/* -------------------------------- Stage IF -------------------------------- */
    ysyx_22050293_StageIF stage_if (
        .clock              (clock),
        .reset              (reset),

        // StageID -> StageIF
        .id2if_allow        (id2if_allow),
        .bj_bus             (bj_bus),

        // StageIF -> StageID
        .if2id_fired        (if2id_fired),
        .if2id_bus          (if2id_bus),

        // StageID -> StageIF @ Cancel Flag
        .id_cancel          (id_cancel),
        // StageEX -> StageIF @ Cancel Flag
        .ex_cancel          (ex_cancel),
        // StageMA -> StageIF @ Cancel Flag
        .ma_cancel          (ma_cancel),
        // StageWB -> StageIF @ Cancel Flag
        .wb_cancel          (wb_cancel),

        // StageID -> StageIF @ fence.i
        .id_fencei_taken    (id_fencei_taken),
        .id_fencei_target   (id_fencei_target),

        // CSR -> StageIF
        .csr_taken          (csr_taken),
        .csr_target         (csr_target),

        // to InstBus
        .ibus_req           (ibus_req),
        .ibus_op            (ibus_op),
        .ibus_size          (ibus_size),
        .ibus_mask          (ibus_mask),
        .ibus_addr          (ibus_addr),
        .ibus_wdata         (ibus_wdata),
        .ibus_rdata         (ibus_rdata),
        .ibus_addr_ok       (ibus_addr_ok),
        .ibus_data_ok       (ibus_data_ok),
        .ibus_uncache       (ibus_uncache),
        .ibus_load_fault    (ibus_load_fault),
        .ibus_store_fault   (ibus_store_fault),
        .ibus_flush         (ibus_flush),
        .ibus_clear         (ibus_clear),
        .ibus_done          (ibus_done),
        .ibus_idle          (ibus_idle)
    );
/* -------------------------------- Stage IF -------------------------------- */


/* -------------------------------- Stage ID -------------------------------- */
    ysyx_22050293_StageID stage_id (
        .clock              (clock),
        .reset              (reset),

        // StageID -> StageIF
        .id2if_allow        (id2if_allow),
        .bj_bus             (bj_bus),

        // StageIF -> StageID
        .if2id_fired        (if2id_fired),
        .if2id_bus          (if2id_bus),

        // StageID -> StageEX
        .id2ex_valid        (id2ex_valid),
        .id2ex_bus          (id2ex_bus),

        // StageEX -> StageID
        .ex2id_allow        (ex2id_allow),

        // StageID -> Regfile
        .id2rf_bus          (id2rf_bus),

        // Regfile -> StageID
        .rf2id_bus          (rf2id_bus),

        // StageID -> StageIF @ Cancel Flag
        .id_cancel          (id_cancel),

        // CSR -> StageID
        .csr_raddr          (csr_raddr),
        .csr_rdata          (csr_rdata),

        // StageEX -> StageID @ Forward & Block
        .ex_rf_hzd_bus      (ex_rf_hzd_bus),
        .ex_csr_blk_bus     (ex_csr_blk_bus),

        // StageMA -> StageID @ Forward & Block
        .ma_rf_hzd_bus      (ma_rf_hzd_bus),
        .ma_csr_blk_bus     (ma_csr_blk_bus),

        // StageWB -> StageID @ Forward & Block
        .wb_rf_hzd_bus      (wb_rf_hzd_bus),
        .wb_csr_blk_bus     (wb_csr_blk_bus),

        // StageID -> StageIF @ fence.i
        .id_fencei_taken    (id_fencei_taken),
        .id_fencei_target   (id_fencei_target),

        // CSR -> StageID
        .csr_taken          (csr_taken),
        .csr_intr           (csr_intr)
    );
/* -------------------------------- Stage ID -------------------------------- */


/* -------------------------------- Stage EX -------------------------------- */
    ysyx_22050293_StageEX stage_ex (
        .clock          (clock),
        .reset          (reset),

        // StageEX -> StageMA
        .ex2ma_valid    (ex2ma_valid),
        .ex2ma_bus      (ex2ma_bus),

        // StageMA -> StageEX
        .ma2ex_allow    (ma2ex_allow),

        // StageEX -> StageID
        .ex2id_allow    (ex2id_allow),

        // StageID -> StageEX
        .id2ex_valid    (id2ex_valid),
        .id2ex_bus      (id2ex_bus),

        // StageEX -> StageIF @ Cancel Flag
        .ex_cancel    (ex_cancel),

        // CSR -> StageEX
        .csr_taken      (csr_taken),

        // StageEX -> StageID @ Forward & Block
        .ex_rf_hzd_bus  (ex_rf_hzd_bus),
        .ex_csr_blk_bus (ex_csr_blk_bus),

        // StageMA -> StageEX @ Cancel Flag
        .ma_cancel      (ma_cancel),

        // StageWB -> StageEX @ Cancel Flag
        .wb_cancel      (wb_cancel),

        // to DataBus
        .dbus_req       (dbus_req),
        .dbus_op        (dbus_op),
        .dbus_size      (dbus_size),
        .dbus_mask      (dbus_mask),
        .dbus_addr      (dbus_addr),
        .dbus_wdata     (dbus_wdata),
        .dbus_addr_ok   (dbus_addr_ok),
        .dbus_uncache   (dbus_uncache),
        .dbus_flush     (dbus_flush),
        .dbus_clear     (dbus_clear),
        .dbus_idle      (dbus_idle)
    );
/* -------------------------------- Stage EX -------------------------------- */


/* -------------------------------- Stage MA -------------------------------- */
    ysyx_22050293_StageMA stage_ma (
        .clock              (clock),
        .reset              (reset),

        // StageMA -> StageEX
        .ma2ex_allow        (ma2ex_allow),

        // StageEX -> StageMA
        .ex2ma_valid        (ex2ma_valid),
        .ex2ma_bus          (ex2ma_bus),

        // StageMA -> StageWB
        .ma2wb_valid        (ma2wb_valid),
        .ma2wb_bus          (ma2wb_bus),

        // StageWB -> StageMA
        .wb2ma_allow        (wb2ma_allow),

        // CSR -> StageMA
        .csr_taken          (csr_taken),

        // StageMA -> StageID @ Forward & Block
        .ma_rf_hzd_bus      (ma_rf_hzd_bus),
        .ma_csr_blk_bus     (ma_csr_blk_bus),

        // StageMA -> StageIF/EX @ Cancel Flag
        .ma_cancel          (ma_cancel),

        // from DataBus
        .dbus_rdata         (dbus_rdata),
        .dbus_data_ok       (dbus_data_ok),
        .dbus_load_fault    (dbus_load_fault),
        .dbus_store_fault   (dbus_store_fault),
        .dbus_done          (dbus_done)
    );
/* -------------------------------- Stage MA -------------------------------- */


/* -------------------------------- Stage WB -------------------------------- */
    ysyx_22050293_StageWB stage_wb (
        .clock              (clock),
        .reset              (reset),

        // StageWB -> Regfile
        .wb2rf_bus          (wb2rf_bus),

        // StageMA -> StageWB
        .ma2wb_valid        (ma2wb_valid),
        .ma2wb_bus          (ma2wb_bus),

        // StageWB -> StageMA
        .wb2ma_allow        (wb2ma_allow),

        // CSR -> StageWB
        .csr_taken          (csr_taken),

        // StageWB -> CSR
        .wb2csr_bus         (wb2csr_bus),

        // StageWB -> StageIF/EX @ Cancel Flag
        .wb_cancel          (wb_cancel),

        // StageWB -> StageID @ Forward & Block
        .wb_rf_hzd_bus      (wb_rf_hzd_bus),
        .wb_csr_blk_bus     (wb_csr_blk_bus)
    );
/* -------------------------------- Stage WB -------------------------------- */

endmodule


module ysyx_22050293 (
    input             clock,
    input             reset,
    input             io_interrupt,
    input             io_master_awready,
    output            io_master_awvalid,
    output [3:0]      io_master_awid,
    output [31:0]     io_master_awaddr,
    output [7:0]      io_master_awlen,
    output [2:0]      io_master_awsize,
    output [1:0]      io_master_awburst,
    input             io_master_wready,
    output            io_master_wvalid,
    output [63:0]     io_master_wdata,
    output [7:0]      io_master_wstrb,
    output            io_master_wlast,
    output            io_master_bready,
    input             io_master_bvalid,
    input [3:0]       io_master_bid,
    input [1:0]       io_master_bresp,
    input             io_master_arready,
    output            io_master_arvalid,
    output [3:0]      io_master_arid,
    output [31:0]     io_master_araddr,
    output [7:0]      io_master_arlen,
    output [2:0]      io_master_arsize,
    output [1:0]      io_master_arburst,
    output            io_master_rready,
    input             io_master_rvalid,
    input [3:0]       io_master_rid,
    input [1:0]       io_master_rresp,
    input [63:0]      io_master_rdata,
    input             io_master_rlast,
    output            io_slave_awready,
    input             io_slave_awvalid,
    input [3:0]       io_slave_awid,
    input [31:0]      io_slave_awaddr,
    input [7:0]       io_slave_awlen,
    input [2:0]       io_slave_awsize,
    input [1:0]       io_slave_awburst,
    output            io_slave_wready,
    input             io_slave_wvalid,
    input [63:0]      io_slave_wdata,
    input [7:0]       io_slave_wstrb,
    input             io_slave_wlast,
    input             io_slave_bready,
    output            io_slave_bvalid,
    output [3:0]      io_slave_bid,
    output [1:0]      io_slave_bresp,
    output            io_slave_arready,
    input             io_slave_arvalid,
    input [3:0]       io_slave_arid,
    input [31:0]      io_slave_araddr,
    input [7:0]       io_slave_arlen,
    input [2:0]       io_slave_arsize,
    input [1:0]       io_slave_arburst,
    input             io_slave_rready,
    output            io_slave_rvalid,
    output [3:0]      io_slave_rid,
    output [1:0]      io_slave_rresp,
    output [63:0]     io_slave_rdata,
    output            io_slave_rlast,
    output [5:0]      io_sram0_addr,
    output            io_sram0_cen,
    output            io_sram0_wen,
    output [127:0]    io_sram0_wmask,
    output [127:0]    io_sram0_wdata,
    input  [127:0]    io_sram0_rdata,
    output [5:0]      io_sram1_addr,
    output            io_sram1_cen,
    output            io_sram1_wen,
    output [127:0]    io_sram1_wmask,
    output [127:0]    io_sram1_wdata,
    input  [127:0]    io_sram1_rdata,
    output [5:0]      io_sram2_addr,
    output            io_sram2_cen,
    output            io_sram2_wen,
    output [127:0]    io_sram2_wmask,
    output [127:0]    io_sram2_wdata,
    input  [127:0]    io_sram2_rdata,
    output [5:0]      io_sram3_addr,
    output            io_sram3_cen,
    output            io_sram3_wen,
    output [127:0]    io_sram3_wmask,
    output [127:0]    io_sram3_wdata,
    input  [127:0]    io_sram3_rdata,
    output [5:0]      io_sram4_addr,
    output            io_sram4_cen,
    output            io_sram4_wen,
    output [127:0]    io_sram4_wmask,
    output [127:0]    io_sram4_wdata,
    input  [127:0]    io_sram4_rdata,
    output [5:0]      io_sram5_addr,
    output            io_sram5_cen,
    output            io_sram5_wen,
    output [127:0]    io_sram5_wmask,
    output [127:0]    io_sram5_wdata,
    input  [127:0]    io_sram5_rdata,
    output [5:0]      io_sram6_addr,
    output            io_sram6_cen,
    output            io_sram6_wen,
    output [127:0]    io_sram6_wmask,
    output [127:0]    io_sram6_wdata,
    input  [127:0]    io_sram6_rdata,
    output [5:0]      io_sram7_addr,
    output            io_sram7_cen,
    output            io_sram7_wen,
    output [127:0]    io_sram7_wmask,
    output [127:0]    io_sram7_wdata,
    input  [127:0]    io_sram7_rdata
);

    // Don't Care
    assign io_slave_awready = 1'b0;
    assign io_slave_wready  = 1'b0;
    assign io_slave_bvalid  = 1'b0;
    assign io_slave_bid     = 4'b0;
    assign io_slave_bresp   = 2'b0;
    assign io_slave_arready = 1'b0;
    assign io_slave_rvalid  = 1'b0;
    assign io_slave_rid     = 4'b0;
    assign io_slave_rresp   = 2'b0;
    assign io_slave_rdata   = 64'b0;
    assign io_slave_rlast   = 1'b0;


/* ***********************************************************************************
    ____________________       _________                       ____________________
    |                   |      |        |                      |                   |
    |                   |<---->| DCache |<-------------------->|                   |
    |                   |      |________|                      |                   |
    |        Core       |      _________       _________       |     AXIBridge     |
    |                   |      |        |      |        |      |                   |
    |                   |<---->| DCache |<---->| DCNode |<---->|                   |
    |___________________|      |________|      |________|      |___________________|
              ^                                     ^
              |                                     |
              |            __________               |
              |            |         |              |
              |------------|  CLINT  |<-------------|
                           |_________|

*********************************************************************************** */

/* ====================== Bridge ====================== */
/* ---------------- ICache ---------------- */
    // Read
    wire            inst_rd_req;
    wire [2:0]      inst_rd_type;
    wire [31:0]     inst_rd_addr;
    wire            inst_rd_rdy;
    wire            inst_ret_valid;
    wire            inst_ret_last;
    wire [127:0]    inst_ret_data;
    wire            inst_read_fault;
    // Write
    wire            inst_wr_req;
    wire [2:0]      inst_wr_type;
    wire [31:0]     inst_wr_addr;
    wire [15:0]     inst_wr_wstrb;
    wire [127:0]    inst_wr_data;
    wire            inst_wr_rdy;
    wire            inst_write_fault;
/* ---------------- ICache ---------------- */

/* ---------------- DCache ---------------- */
    // Master Port
    // Read
    wire            m_data_rd_req;
    wire [2:0]      m_data_rd_type;
    wire [31:0]     m_data_rd_addr;
    wire            m_data_rd_rdy;
    wire            m_data_ret_valid;
    wire            m_data_ret_last;
    wire [127:0]    m_data_ret_data;
    wire            m_data_read_fault;
    // Write
    wire            m_data_wr_req;
    wire [2:0]      m_data_wr_type;
    wire [31:0]     m_data_wr_addr;
    wire [15:0]     m_data_wr_wstrb;
    wire [127:0]    m_data_wr_data;
    wire            m_data_wr_rdy;
    wire            m_data_write_fault;

    // Slave Port
    // Read
    wire            s_data_rd_req;
    wire [2:0]      s_data_rd_type;
    wire [31:0]     s_data_rd_addr;
    wire            s_data_rd_rdy;
    wire            s_data_ret_valid;
    wire            s_data_ret_last;
    wire [127:0]    s_data_ret_data;
    wire            s_data_read_fault;
    // Write
    wire            s_data_wr_req;
    wire [2:0]      s_data_wr_type;
    wire [31:0]     s_data_wr_addr;
    wire [15:0]     s_data_wr_wstrb;
    wire [127:0]    s_data_wr_data;
    wire            s_data_wr_rdy;
    wire            s_data_write_fault;

/* ---------------- DCache ---------------- */

    ysyx_22050293_AXI4Bridge bridge (
        .clock              (clock),
        .reset              (reset),
        /* ********* AXI Master Interface ********* */
        // aw
        .io_master_awid           (io_master_awid),
        .io_master_awaddr         (io_master_awaddr),
        .io_master_awlen          (io_master_awlen),
        .io_master_awsize         (io_master_awsize),
        .io_master_awburst        (io_master_awburst),
        .io_master_awvalid        (io_master_awvalid),
        .io_master_awready        (io_master_awready),
        // w
        .io_master_wdata          (io_master_wdata),
        .io_master_wstrb          (io_master_wstrb),
        .io_master_wlast          (io_master_wlast),
        .io_master_wvalid         (io_master_wvalid),
        .io_master_wready         (io_master_wready),
        // b
        .io_master_bid            (io_master_bid),
        .io_master_bresp          (io_master_bresp),
        .io_master_bvalid         (io_master_bvalid),
        .io_master_bready         (io_master_bready),
        // ar
        .io_master_arid           (io_master_arid),
        .io_master_araddr         (io_master_araddr),
        .io_master_arlen          (io_master_arlen),
        .io_master_arsize         (io_master_arsize),
        .io_master_arburst        (io_master_arburst),
        .io_master_arvalid        (io_master_arvalid),
        .io_master_arready        (io_master_arready),
        // r
        .io_master_rid            (io_master_rid),
        .io_master_rdata          (io_master_rdata),
        .io_master_rresp          (io_master_rresp),
        .io_master_rlast          (io_master_rlast),
        .io_master_rvalid         (io_master_rvalid),
        .io_master_rready         (io_master_rready),
        /* ********* AXI Master Interface ********* */

        /* ********* ICache Interface ********* */
        // Read
        .inst_rd_req        (inst_rd_req),
        .inst_rd_type       (inst_rd_type),
        .inst_rd_addr       (inst_rd_addr),
        .inst_rd_rdy        (inst_rd_rdy),
        .inst_ret_valid     (inst_ret_valid),
        .inst_ret_last      (inst_ret_last),
        .inst_ret_data      (inst_ret_data),
        .inst_read_fault    (inst_read_fault),
        // Write
        .inst_wr_req        (inst_wr_req),
        .inst_wr_type       (inst_wr_type),
        .inst_wr_addr       (inst_wr_addr),
        .inst_wr_wstrb      (inst_wr_wstrb),
        .inst_wr_data       (inst_wr_data),
        .inst_wr_rdy        (inst_wr_rdy),
        .inst_write_fault   (inst_write_fault),
        /* ********* ICache Interface ********* */

        /* ********* DCache Interface ********* */
        // Read
        .data_rd_req        (s_data_rd_req),
        .data_rd_type       (s_data_rd_type),
        .data_rd_addr       (s_data_rd_addr),
        .data_rd_rdy        (s_data_rd_rdy),
        .data_ret_valid     (s_data_ret_valid),
        .data_ret_last      (s_data_ret_last),
        .data_ret_data      (s_data_ret_data),
        .data_read_fault    (s_data_read_fault),
        // Write
        .data_wr_req        (s_data_wr_req),
        .data_wr_type       (s_data_wr_type),
        .data_wr_addr       (s_data_wr_addr),
        .data_wr_wstrb      (s_data_wr_wstrb),
        .data_wr_data       (s_data_wr_data),
        .data_wr_rdy        (s_data_wr_rdy),
        .data_write_fault   (s_data_write_fault)
        /* ********* DCache Interface ********* */
    );
/* ====================== Bridge ====================== */


/* ================= CLINT ================= */
    wire           clint_cen;
    wire           clint_wen;
    wire [31:0]    clint_addr;
    wire [63:0]    clint_wdata;
    wire [63:0]    clint_rdata;
    wire           clint_valid;
    wire           clint_fault;
    wire [1:0]     clint_intr;

    ysyx_22050293_CLINT clint (
        .clock          (clock),
        .reset          (reset),
        .cen            (clint_cen),
        .wen            (clint_wen),
        .addr           (clint_addr),
        .wdata          (clint_wdata),
        .rdata          (clint_rdata),
        .valid          (clint_valid),
        .fault          (clint_fault),
        .intr           (clint_intr)
    );
/* ================= CLINT ================= */


/* ================= DCNode ================= */
    ysyx_22050293_DCNode dcnode (
        /* ********* DCNode <-> Cache ********* */
        // Read
        .m_rd_req       (m_data_rd_req),
        .m_rd_type      (m_data_rd_type),
        .m_rd_addr      (m_data_rd_addr),
        .m_rd_rdy       (m_data_rd_rdy),
        .m_ret_valid    (m_data_ret_valid),
        .m_ret_last     (m_data_ret_last),
        .m_ret_data     (m_data_ret_data),
        .m_read_fault   (m_data_read_fault),
        // Write
        .m_wr_req       (m_data_wr_req),
        .m_wr_type      (m_data_wr_type),
        .m_wr_addr      (m_data_wr_addr),
        .m_wr_wstrb     (m_data_wr_wstrb),
        .m_wr_data      (m_data_wr_data),
        .m_wr_rdy       (m_data_wr_rdy),
        .m_write_fault  (m_data_write_fault),
        /* ********* DCNode <-> Cache ********* */

        /* ********* DCNode <-> AXI ********* */
        // Read
        .s_rd_req       (s_data_rd_req),
        .s_rd_type      (s_data_rd_type),
        .s_rd_addr      (s_data_rd_addr),
        .s_rd_rdy       (s_data_rd_rdy),
        .s_ret_valid    (s_data_ret_valid),
        .s_ret_last     (s_data_ret_last),
        .s_ret_data     (s_data_ret_data),
        .s_read_fault   (s_data_read_fault),
        // Write
        .s_wr_req       (s_data_wr_req),
        .s_wr_type      (s_data_wr_type),
        .s_wr_addr      (s_data_wr_addr),
        .s_wr_wstrb     (s_data_wr_wstrb),
        .s_wr_data      (s_data_wr_data),
        .s_wr_rdy       (s_data_wr_rdy),
        .s_write_fault  (s_data_write_fault),
        /* ********* DCNode <-> AXI ********* */

        /* ********* DCNode <-> CLINT ********* */
        .clint_cen      (clint_cen),
        .clint_wen      (clint_wen),
        .clint_addr     (clint_addr),
        .clint_wdata    (clint_wdata),
        .clint_rdata    (clint_rdata),
        .clint_valid    (clint_valid),
        .clint_fault    (clint_fault)
        /* ********* DCNode <-> CLINT ********* */
    );
/* ================= DCNode ================= */


/* ================= Cache ================= */
    wire           ibus_req;
    wire           ibus_op;
    wire [2:0]     ibus_size;
    wire [7:0]     ibus_mask;
    wire [31:0]    ibus_addr;
    wire [63:0]    ibus_wdata;
    wire [63:0]    ibus_rdata;
    wire           ibus_addr_ok;
    wire           ibus_data_ok;
    wire           ibus_uncache;
    wire           ibus_load_fault;
    wire           ibus_store_fault;
    wire           ibus_flush;
    wire           ibus_clear;
    wire           ibus_done;
    wire           ibus_idle;

    wire           dbus_req;
    wire           dbus_op;
    wire [2:0]     dbus_size;
    wire [7:0]     dbus_mask;
    wire [31:0]    dbus_addr;
    wire [63:0]    dbus_wdata;
    wire [63:0]    dbus_rdata;
    wire           dbus_addr_ok;
    wire           dbus_data_ok;
    wire           dbus_uncache;
    wire           dbus_load_fault;
    wire           dbus_store_fault;
    wire           dbus_flush;
    wire           dbus_clear;
    wire           dbus_done;
    wire           dbus_idle;

    ysyx_22050293_Cache icache (
        .clock          (clock),
        .reset          (reset),

        /* ********* Cache <-> SRAM ********* */
        // SRAM 0
        .sram0_addr      (io_sram0_addr),
        .sram0_cen       (io_sram0_cen),
        .sram0_wen       (io_sram0_wen),
        .sram0_wmask     (io_sram0_wmask),
        .sram0_wdata     (io_sram0_wdata),
        .sram0_rdata     (io_sram0_rdata),
        // SRAM 1
        .sram1_addr      (io_sram1_addr),
        .sram1_cen       (io_sram1_cen),
        .sram1_wen       (io_sram1_wen),
        .sram1_wmask     (io_sram1_wmask),
        .sram1_wdata     (io_sram1_wdata),
        .sram1_rdata     (io_sram1_rdata),
        // SRAM 2
        .sram2_addr      (io_sram2_addr),
        .sram2_cen       (io_sram2_cen),
        .sram2_wen       (io_sram2_wen),
        .sram2_wmask     (io_sram2_wmask),
        .sram2_wdata     (io_sram2_wdata),
        .sram2_rdata     (io_sram2_rdata),
        // SRAM 3
        .sram3_addr      (io_sram3_addr),
        .sram3_cen       (io_sram3_cen),
        .sram3_wen       (io_sram3_wen),
        .sram3_wmask     (io_sram3_wmask),
        .sram3_wdata     (io_sram3_wdata),
        .sram3_rdata     (io_sram3_rdata),
        /* ********* Cache <-> SRAM ********* */

        /* ********* Cache <-> CPU ********* */
        .request        (ibus_req),
        .op             (ibus_op),
        .tag            (ibus_addr[31:11]),
        .index          (ibus_addr[10:4]),
        .offset         (ibus_addr[3:0]),
        .wstrb          (ibus_mask),
        .wdata          (ibus_wdata),
        .addr_ok        (ibus_addr_ok),
        .data_ok        (ibus_data_ok),
        .rdata          (ibus_rdata),
        .size           (ibus_size),
        .uncache        (ibus_uncache),
        .load_fault     (ibus_load_fault),
        .store_fault    (ibus_store_fault),
        .flush          (ibus_flush),
        .clear          (ibus_clear),
        .done           (ibus_done),
        .idle           (ibus_idle),
        /* ********* Cache <-> CPU ********* */

        /* ********* Cache <-> AXI ********* */
        // AXI Read
        .rd_req         (inst_rd_req),
        .rd_type        (inst_rd_type),
        .rd_addr        (inst_rd_addr),
        .rd_rdy         (inst_rd_rdy),
        .ret_valid      (inst_ret_valid),
        .ret_last       (inst_ret_last),
        .ret_data       (inst_ret_data),
        .read_fault     (inst_read_fault),
        // AXI Write
        .wr_req         (inst_wr_req),
        .wr_type        (inst_wr_type),
        .wr_addr        (inst_wr_addr),
        .wr_wstrb       (inst_wr_wstrb),
        .wr_data        (inst_wr_data),
        .wr_rdy         (inst_wr_rdy),
        .write_fault    (inst_write_fault)
        /* ********* Cache <-> AXI ********* */
    );

    ysyx_22050293_Cache dcache (
        .clock          (clock),
        .reset          (reset),

        /* ********* Cache <-> SRAM ********* */
        // SRAM 0
        .sram0_addr      (io_sram4_addr),
        .sram0_cen       (io_sram4_cen),
        .sram0_wen       (io_sram4_wen),
        .sram0_wmask     (io_sram4_wmask),
        .sram0_wdata     (io_sram4_wdata),
        .sram0_rdata     (io_sram4_rdata),
        // SRAM 1
        .sram1_addr      (io_sram5_addr),
        .sram1_cen       (io_sram5_cen),
        .sram1_wen       (io_sram5_wen),
        .sram1_wmask     (io_sram5_wmask),
        .sram1_wdata     (io_sram5_wdata),
        .sram1_rdata     (io_sram5_rdata),
        // SRAM 2
        .sram2_addr      (io_sram6_addr),
        .sram2_cen       (io_sram6_cen),
        .sram2_wen       (io_sram6_wen),
        .sram2_wmask     (io_sram6_wmask),
        .sram2_wdata     (io_sram6_wdata),
        .sram2_rdata     (io_sram6_rdata),
        // SRAM 3
        .sram3_addr      (io_sram7_addr),
        .sram3_cen       (io_sram7_cen),
        .sram3_wen       (io_sram7_wen),
        .sram3_wmask     (io_sram7_wmask),
        .sram3_wdata     (io_sram7_wdata),
        .sram3_rdata     (io_sram7_rdata),
        /* ********* Cache <-> SRAM ********* */

        /* ********* Cache <-> CPU ********* */
        .request        (dbus_req),
        .op             (dbus_op),
        .tag            (dbus_addr[31:11]),
        .index          (dbus_addr[10:4]),
        .offset         (dbus_addr[3:0]),
        .wstrb          (dbus_mask),
        .wdata          (dbus_wdata),
        .addr_ok        (dbus_addr_ok),
        .data_ok        (dbus_data_ok),
        .rdata          (dbus_rdata),
        .size           (dbus_size),
        .uncache        (dbus_uncache),
        .load_fault     (dbus_load_fault),
        .store_fault    (dbus_store_fault),
        .flush          (dbus_flush),
        .clear          (dbus_clear),
        .done           (dbus_done),
        .idle           (dbus_idle),
        /* ********* Cache <-> CPU ********* */

        /* ********* Cache <-> AXI ********* */
        // AXI Read
        .rd_req         (m_data_rd_req),
        .rd_type        (m_data_rd_type),
        .rd_addr        (m_data_rd_addr),
        .rd_rdy         (m_data_rd_rdy),
        .ret_valid      (m_data_ret_valid),
        .ret_last       (m_data_ret_last),
        .ret_data       (m_data_ret_data),
        .read_fault     (m_data_read_fault),
        // AXI Write
        .wr_req         (m_data_wr_req),
        .wr_type        (m_data_wr_type),
        .wr_addr        (m_data_wr_addr),
        .wr_wstrb       (m_data_wr_wstrb),
        .wr_data        (m_data_wr_data),
        .wr_rdy         (m_data_wr_rdy),
        .write_fault    (m_data_write_fault)
        /* ********* Cache <-> AXI ********* */
    );
/* ================= Cache ================= */

/* ================== Core ================== */
    ysyx_22050293_Core core (
        .clock              (clock),
        .reset              (reset),
        // Interrupts
        .clint_intr         (clint_intr),
        // InstBus interface
        .ibus_req           (ibus_req),
        .ibus_op            (ibus_op),
        .ibus_size          (ibus_size),
        .ibus_mask          (ibus_mask),
        .ibus_addr          (ibus_addr),
        .ibus_wdata         (ibus_wdata),
        .ibus_rdata         (ibus_rdata),
        .ibus_addr_ok       (ibus_addr_ok),
        .ibus_data_ok       (ibus_data_ok),
        .ibus_uncache       (ibus_uncache),
        .ibus_load_fault    (ibus_load_fault),
        .ibus_store_fault   (ibus_store_fault),
        .ibus_flush         (ibus_flush),
        .ibus_clear         (ibus_clear),
        .ibus_done          (ibus_done),
        .ibus_idle          (ibus_idle),
        // DataBus interface
        .dbus_req           (dbus_req),
        .dbus_op            (dbus_op),
        .dbus_size          (dbus_size),
        .dbus_mask          (dbus_mask),
        .dbus_addr          (dbus_addr),
        .dbus_wdata         (dbus_wdata),
        .dbus_rdata         (dbus_rdata),
        .dbus_addr_ok       (dbus_addr_ok),
        .dbus_data_ok       (dbus_data_ok),
        .dbus_uncache       (dbus_uncache),
        .dbus_load_fault    (dbus_load_fault),
        .dbus_store_fault   (dbus_store_fault),
        .dbus_flush         (dbus_flush),
        .dbus_clear         (dbus_clear),
        .dbus_done          (dbus_done),
        .dbus_idle          (dbus_idle)
    );
/* ================== Core ================== */
endmodule
